feat(ui): uniformise les droplist dynamiques sur le style du picker d'agent
Introduit SmallDropdown comme composant partagé et le fait adopter par AgentsPanel, ModelServerSelect, OpenCodeModeFields, TemplateEditor et TicketViewportSelect, pour que toutes les listes déroulantes dynamiques (agent, modèle, template, viewport) partagent la même apparence que le sélecteur d'agent de la création de ticket. Refs #114 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import type { LocalModelServerConfig } from "@/domain";
|
||||
import { cn } from "@/shared";
|
||||
import { SmallDropdown } from "@/shared";
|
||||
|
||||
/** The sentinel option value standing for "no managed server". */
|
||||
const NONE = "";
|
||||
@ -34,24 +34,20 @@ export function ModelServerSelect({
|
||||
value !== undefined && !servers.some((s) => s.id === value);
|
||||
|
||||
return (
|
||||
<select
|
||||
<SmallDropdown
|
||||
aria-label={ariaLabel}
|
||||
value={value ?? NONE}
|
||||
onChange={(e) => onChange(e.target.value === NONE ? undefined : e.target.value)}
|
||||
className={cn(
|
||||
"h-8 rounded-md bg-raised px-2 text-xs text-content",
|
||||
"border border-border outline-none focus:border-primary",
|
||||
)}
|
||||
>
|
||||
<option value={NONE}>None (external endpoint)</option>
|
||||
{servers.map((s) => (
|
||||
<option key={s.id} value={s.id}>
|
||||
{s.name} — {s.servedModelName}
|
||||
</option>
|
||||
))}
|
||||
{missing && (
|
||||
<option value={value}>{value} (unknown / removed)</option>
|
||||
)}
|
||||
</select>
|
||||
onChange={(next) => onChange(next === NONE ? undefined : next)}
|
||||
options={[
|
||||
{ value: NONE, label: "None (external endpoint)" },
|
||||
...servers.map((s) => ({
|
||||
value: s.id,
|
||||
label: `${s.name} — ${s.servedModelName}`,
|
||||
})),
|
||||
...(missing && value !== undefined
|
||||
? [{ value, label: `${value} (unknown / removed)` }]
|
||||
: []),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -40,6 +40,11 @@ const SERVER: LocalModelServerConfig = {
|
||||
stopPolicy: "stopOnAppExit",
|
||||
};
|
||||
|
||||
function chooseDropdownOption(label: string | RegExp, optionName: string | RegExp) {
|
||||
fireEvent.click(screen.getByLabelText(label));
|
||||
fireEvent.click(screen.getByRole("option", { name: optionName }));
|
||||
}
|
||||
|
||||
function setup(modelServer = new MockModelServerGateway()) {
|
||||
const gateways = { modelServer } as unknown as Gateways;
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
@ -433,9 +438,10 @@ describe("ModelServerSelect (F35)", () => {
|
||||
onChange={(id) => (picked = id)}
|
||||
/>,
|
||||
);
|
||||
const select = screen.getByLabelText("local model server") as HTMLSelectElement;
|
||||
expect(select.value).toBe("");
|
||||
fireEvent.change(select, { target: { value: SERVER.id } });
|
||||
expect(screen.getByLabelText("local model server").textContent).toContain(
|
||||
"None (external endpoint)",
|
||||
);
|
||||
chooseDropdownOption("local model server", /Local A/);
|
||||
expect(picked).toBe(SERVER.id);
|
||||
});
|
||||
|
||||
@ -448,9 +454,7 @@ describe("ModelServerSelect (F35)", () => {
|
||||
onChange={(id) => (picked = id)}
|
||||
/>,
|
||||
);
|
||||
fireEvent.change(screen.getByLabelText("local model server"), {
|
||||
target: { value: "" },
|
||||
});
|
||||
chooseDropdownOption("local model server", "None (external endpoint)");
|
||||
expect(picked).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user