Skip to content
Open
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
10 changes: 10 additions & 0 deletions clients/web/src/components/groups/AppControls/AppControls.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
CloseButton,
Group,
ScrollArea,
Stack,
Expand Down Expand Up @@ -61,6 +62,15 @@ export function AppControls({
placeholder="Search apps..."
value={searchText}
onChange={(e) => onSearchChange(e.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
searchText ? (
<CloseButton
aria-label="Clear"
onClick={() => onSearchChange("")}
/>
) : null
}
/>
<ScrollArea.Autosize viewportRef={viewportRef} mah={LIST_MAX_HEIGHT}>
<Stack gap="xs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Button,
Card,
Checkbox,
CloseButton,
Divider,
Group,
Stack,
Expand Down Expand Up @@ -244,13 +245,31 @@ export function ExperimentalFeaturesPanel({
onChange={(e) =>
onHeaderChange(index, e.currentTarget.value, header.value)
}
rightSectionPointerEvents="auto"
rightSection={
header.key ? (
<CloseButton
aria-label="Clear"
onClick={() => onHeaderChange(index, "", header.value)}
/>
) : null
}
/>
<TextInput
placeholder="Header value"
value={header.value}
onChange={(e) =>
onHeaderChange(index, header.key, e.currentTarget.value)
}
rightSectionPointerEvents="auto"
rightSection={
header.value ? (
<CloseButton
aria-label="Clear"
onClick={() => onHeaderChange(index, header.key, "")}
/>
) : null
}
/>
<RemoveIcon onClick={() => onRemoveHeader(index)}>
<Text size="xs">✕</Text>
Expand All @@ -269,6 +288,15 @@ export function ExperimentalFeaturesPanel({
onChange={(e) => onRequestChange(e.currentTarget.value)}
autosize
minRows={6}
rightSectionPointerEvents="auto"
rightSection={
requestDraft ? (
<CloseButton
aria-label="Clear"
onClick={() => onRequestChange("")}
/>
) : null
}
/>

<Button onClick={onSendRequest}>Send Request</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ describe("HistoryControls", () => {
expect(onSearchChange).toHaveBeenCalledWith("a");
});

it("clears the search input when the Clear button is clicked", async () => {
const user = userEvent.setup();
const onSearchChange = vi.fn();
renderWithMantine(
<HistoryControls
{...baseProps}
searchText="abc"
onSearchChange={onSearchChange}
/>,
);
await user.click(screen.getByRole("button", { name: "Clear" }));
expect(onSearchChange).toHaveBeenCalledWith("");
});

it("renders the method filter placeholder", () => {
renderWithMantine(<HistoryControls {...baseProps} />);
expect(screen.getByPlaceholderText("All methods")).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Select, Stack, TextInput, Title } from "@mantine/core";
import { CloseButton, Select, Stack, TextInput, Title } from "@mantine/core";
import type {
MessageMethod,
MessageOrigin,
Expand Down Expand Up @@ -33,6 +33,15 @@ export function HistoryControls({
placeholder="Search..."
value={searchText}
onChange={(event) => onSearchChange(event.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
searchText ? (
<CloseButton
aria-label="Clear"
onClick={() => onSearchChange("")}
/>
) : null
}
/>

<Title order={6}>Filter by Method</Title>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,40 @@ describe("ImportServerJsonPanel", () => {
expect(onServerNameChange).toHaveBeenCalledWith("X");
});

it("clears the paste textarea, env-var, and name-override fields via Clear buttons", async () => {
const user = userEvent.setup();
const onJsonChange = vi.fn();
const onEnvVarChange = vi.fn();
const onServerNameChange = vi.fn();
const envVars: EnvVarInfo[] = [
{ name: "DEBUG", required: false, value: "true" },
];
renderWithMantine(
<ImportServerJsonPanel
{...baseHandlers}
onJsonChange={onJsonChange}
onEnvVarChange={onEnvVarChange}
onServerNameChange={onServerNameChange}
draft={{
rawText: "{}",
envOverrides: {},
nameOverride: "Custom Name",
}}
validation={[]}
envVars={envVars}
/>,
);
// DOM order: paste textarea, env-var input, name-override input.
const clearButtons = screen.getAllByRole("button", { name: "Clear" });
expect(clearButtons).toHaveLength(3);
await user.click(clearButtons[0]);
expect(onJsonChange).toHaveBeenCalledWith("");
await user.click(clearButtons[1]);
expect(onEnvVarChange).toHaveBeenCalledWith("DEBUG", "");
await user.click(clearButtons[2]);
expect(onServerNameChange).toHaveBeenCalledWith("");
});

it("renders the existing nameOverride value", () => {
renderWithMantine(
<ImportServerJsonPanel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Button,
CloseButton,
Divider,
Group,
Radio,
Expand Down Expand Up @@ -88,6 +89,12 @@ export function ImportServerJsonPanel({
autosize
minRows={8}
maxRows={15}
rightSectionPointerEvents="auto"
rightSection={
draft.rawText ? (
<CloseButton aria-label="Clear" onClick={() => onJsonChange("")} />
) : null
}
/>

<Divider />
Expand Down Expand Up @@ -139,6 +146,15 @@ export function ImportServerJsonPanel({
onChange={(e) =>
onEnvVarChange(envVar.name, e.currentTarget.value)
}
rightSectionPointerEvents="auto"
rightSection={
envVar.value ? (
<CloseButton
aria-label="Clear"
onClick={() => onEnvVarChange(envVar.name, "")}
/>
) : null
}
/>
))}
</>
Expand All @@ -150,6 +166,15 @@ export function ImportServerJsonPanel({
label="Server Name (optional override)"
value={draft.nameOverride ?? ""}
onChange={(e) => onServerNameChange(e.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
draft.nameOverride ? (
<CloseButton
aria-label="Clear"
onClick={() => onServerNameChange("")}
/>
) : null
}
/>

<Group justify="flex-end">
Expand Down
10 changes: 10 additions & 0 deletions clients/web/src/components/groups/LogControls/LogControls.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Button,
CloseButton,
Group,
Select,
Stack,
Expand Down Expand Up @@ -64,6 +65,15 @@ export function LogControls({
placeholder="Search..."
value={filterText}
onChange={(e) => onFilterChange(e.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
filterText ? (
<CloseButton
aria-label="Clear"
onClick={() => onFilterChange("")}
/>
) : null
}
/>

<Title order={5}>Set Active Level</Title>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Button,
CloseButton,
Group,
Stack,
Text,
Expand Down Expand Up @@ -45,6 +46,15 @@ export function NetworkControls({
placeholder="Search..."
value={filterText}
onChange={(e) => onFilterChange(e.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
filterText ? (
<CloseButton
aria-label="Clear"
onClick={() => onFilterChange("")}
/>
) : null
}
/>

<Group justify="space-between">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,23 @@ describe("PromptArgumentsForm", () => {
expect(onArgumentChange).toHaveBeenCalledWith("text", "h");
});

it("clears an argument via its Clear button (onArgumentChange with empty value)", async () => {
const user = userEvent.setup();
const onArgumentChange = vi.fn();
renderWithMantine(
<PromptArgumentsForm
prompt={promptWithArgs}
argumentValues={{ text: "Hello" }}
onArgumentChange={onArgumentChange}
onGetPrompt={vi.fn()}
/>,
);
// Non-autocomplete branch (completions unsupported) renders a TextInput
// with a Clear button whenever the value is non-empty.
await user.click(screen.getByRole("button", { name: "Clear" }));
expect(onArgumentChange).toHaveBeenCalledWith("text", "");
});

it("invokes onGetPrompt when Get Prompt is clicked", async () => {
const user = userEvent.setup();
const onGetPrompt = vi.fn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
import {
Autocomplete,
Button,
CloseButton,
Group,
Stack,
Text,
Expand Down Expand Up @@ -221,6 +222,15 @@ export function PromptArgumentsForm({
onChange={(event) =>
handleChange(arg.name, event.currentTarget.value)
}
rightSectionPointerEvents="auto"
rightSection={
argumentValues[arg.name] ? (
<CloseButton
aria-label="Clear"
onClick={() => handleChange(arg.name, "")}
/>
) : null
}
/>
),
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Group, ScrollArea, Stack, TextInput, Title } from "@mantine/core";
import {
CloseButton,
Group,
ScrollArea,
Stack,
TextInput,
Title,
} from "@mantine/core";
import type { Prompt } from "@modelcontextprotocol/sdk/types.js";
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
import { PromptListItem } from "../PromptListItem/PromptListItem";
Expand Down Expand Up @@ -47,6 +54,15 @@ export function PromptControls({
placeholder="Search prompts..."
value={searchText}
onChange={(e) => onSearchChange(e.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
searchText ? (
<CloseButton
aria-label="Clear"
onClick={() => onSearchChange("")}
/>
) : null
}
/>
<ScrollArea.Autosize viewportRef={viewportRef} mah={LIST_MAX_HEIGHT}>
<Stack gap="xs">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Accordion,
CloseButton,
Group,
ScrollArea,
Stack,
Expand Down Expand Up @@ -127,6 +128,15 @@ export function ResourceControls({
placeholder="Search..."
value={searchText}
onChange={(e) => onSearchChange(e.currentTarget.value)}
rightSectionPointerEvents="auto"
rightSection={
searchText ? (
<CloseButton
aria-label="Clear"
onClick={() => onSearchChange("")}
/>
) : null
}
/>
<ListToggle compact={!allExpanded} onToggle={handleToggleList} />
</Group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ describe("ResourceTemplatePanel", () => {
expect(screen.getByText("file:///users/bob/profile")).toBeInTheDocument();
});

it("clears a variable via its Clear button (non-autocomplete branch)", async () => {
const user = userEvent.setup();
renderWithMantine(
<ResourceTemplatePanel
template={singleVarTemplate}
onReadResource={vi.fn()}
/>,
);
const input = screen.getByLabelText("userId");
await user.type(input, "alice");
expect(input).toHaveValue("alice");
// The Clear button only renders while the value is non-empty.
await user.click(screen.getByRole("button", { name: "Clear" }));
expect(input).toHaveValue("");
});

it("renders annotation badges when present", () => {
renderWithMantine(
<ResourceTemplatePanel
Expand Down
Loading
Loading