mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-26 02:00:52 +01:00
Tighten issue service quick controls
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
4de9bc3451
commit
3bf8cfd127
4 files changed
+39
-13
No files matched your search
@@ -772,13 +772,17 @@ describe("IssueProperties", () => {
|
||||
});
|
||||
await flush();
|
||||
|
||||
expect(container.querySelector(`a[href="${serviceUrl}"]`)).not.toBeNull();
|
||||
const serviceLink = container.querySelector(`a[href="${serviceUrl}"]`);
|
||||
expect(serviceLink).not.toBeNull();
|
||||
expect(serviceLink?.className).toContain("sm:self-start");
|
||||
expect(serviceLink?.className).not.toContain("sm:self-end");
|
||||
expect((container.textContent ?? "").indexOf("Workspace")).toBeLessThan(
|
||||
(container.textContent ?? "").indexOf("Service"),
|
||||
);
|
||||
const stopButton = Array.from(container.querySelectorAll("button"))
|
||||
.find((button) => button.textContent?.trim() === "Stop");
|
||||
const stopButton = container.querySelector<HTMLButtonElement>('button[aria-label="Stop"]');
|
||||
expect(stopButton).not.toBeUndefined();
|
||||
expect(stopButton?.getAttribute("data-size")).toBe("icon-xs");
|
||||
expect(stopButton?.getAttribute("data-variant")).toBe("outline");
|
||||
|
||||
await act(async () => {
|
||||
stopButton!.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
|
||||
@@ -2220,6 +2220,8 @@ export function IssueProperties({
|
||||
pendingRequest={pendingWorkspaceRuntimeAction}
|
||||
onAction={(request) => controlWorkspaceRuntime.mutate(request)}
|
||||
square
|
||||
align="start"
|
||||
iconOnly
|
||||
/>
|
||||
{runtimeActionMessage ? (
|
||||
<span className="text-xs text-muted-foreground" role="status">{runtimeActionMessage}</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { act } from "react";
|
||||
import { flushSync } from "react-dom";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import type { WorkspaceRuntimeService } from "@paperclipai/shared";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
@@ -14,6 +14,10 @@ import {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
function act(callback: () => void) {
|
||||
flushSync(callback);
|
||||
}
|
||||
|
||||
function createRuntimeService(overrides: Partial<WorkspaceRuntimeService> = {}): WorkspaceRuntimeService {
|
||||
return {
|
||||
id: overrides.id ?? "service-1",
|
||||
|
||||
@@ -226,12 +226,14 @@ function CommandActionButtons({
|
||||
pendingRequest,
|
||||
onAction,
|
||||
square,
|
||||
iconOnly,
|
||||
}: {
|
||||
item: WorkspaceRuntimeControlItem;
|
||||
isPending: boolean;
|
||||
pendingRequest: WorkspaceRuntimeControlRequest | null | undefined;
|
||||
onAction: (request: WorkspaceRuntimeControlRequest) => void;
|
||||
square?: boolean;
|
||||
iconOnly?: boolean;
|
||||
}) {
|
||||
const actions: WorkspaceRuntimeAction[] =
|
||||
item.kind === "job"
|
||||
@@ -241,7 +243,7 @@ function CommandActionButtons({
|
||||
: ["start"];
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap">
|
||||
<div className={cn("flex gap-2", iconOnly ? "w-auto flex-row flex-wrap" : "w-full flex-col sm:w-auto sm:flex-row sm:flex-wrap")}>
|
||||
{actions.map((action) => {
|
||||
const request = buildRequest(item, action);
|
||||
const Icon = action === "stop" ? Square : action === "restart" ? RotateCcw : Play;
|
||||
@@ -261,17 +263,19 @@ function CommandActionButtons({
|
||||
return (
|
||||
<Button
|
||||
key={`${item.key}:${action}`}
|
||||
variant={action === "stop" ? "destructive" : action === "restart" ? "outline" : "default"}
|
||||
size="sm"
|
||||
variant={iconOnly ? "outline" : action === "stop" ? "destructive" : action === "restart" ? "outline" : "default"}
|
||||
size={iconOnly ? "icon-xs" : "sm"}
|
||||
className={cn(
|
||||
"w-full justify-start sm:w-auto",
|
||||
iconOnly ? "shrink-0" : "w-full justify-start sm:w-auto",
|
||||
square ? "rounded-none" : null,
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={() => onAction(request)}
|
||||
aria-label={label}
|
||||
title={label}
|
||||
>
|
||||
{showSpinner ? <Loader2 className="h-4 w-4 animate-spin" /> : <Icon className="h-4 w-4" />}
|
||||
{label}
|
||||
{iconOnly ? <span className="sr-only">{label}</span> : label}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
@@ -289,6 +293,7 @@ function CommandSection({
|
||||
pendingRequest,
|
||||
onAction,
|
||||
square,
|
||||
iconOnly,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
@@ -299,6 +304,7 @@ function CommandSection({
|
||||
pendingRequest: WorkspaceRuntimeControlRequest | null | undefined;
|
||||
onAction: (request: WorkspaceRuntimeControlRequest) => void;
|
||||
square?: boolean;
|
||||
iconOnly?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
@@ -330,6 +336,7 @@ function CommandSection({
|
||||
pendingRequest={pendingRequest}
|
||||
onAction={onAction}
|
||||
square={square}
|
||||
iconOnly={iconOnly}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1 text-xs text-muted-foreground">
|
||||
@@ -466,24 +473,29 @@ export function WorkspaceRuntimeQuickControls({
|
||||
pendingRequest = null,
|
||||
onAction,
|
||||
square,
|
||||
align = "end",
|
||||
iconOnly = false,
|
||||
}: {
|
||||
sections: WorkspaceRuntimeControlSections;
|
||||
isPending?: boolean;
|
||||
pendingRequest?: WorkspaceRuntimeControlRequest | null;
|
||||
onAction: (request: WorkspaceRuntimeControlRequest) => void;
|
||||
square?: boolean;
|
||||
align?: "start" | "end";
|
||||
iconOnly?: boolean;
|
||||
}) {
|
||||
const controlItems = sections.services.length > 0 ? sections.services : sections.otherServices;
|
||||
const serviceUrl = getRunningRuntimeServiceUrl(sections);
|
||||
const alignEnd = align === "end";
|
||||
|
||||
if (controlItems.length === 0 && !serviceUrl) return null;
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 flex-col items-stretch gap-2 sm:items-end">
|
||||
<div className={cn("flex min-w-0 flex-col items-stretch gap-2", alignEnd ? "sm:items-end" : "sm:items-start")}>
|
||||
{controlItems.length > 0 ? (
|
||||
<div className="flex max-w-full flex-col gap-2 sm:flex-row sm:flex-wrap sm:justify-end">
|
||||
<div className={cn("flex max-w-full flex-col gap-2 sm:flex-row sm:flex-wrap", alignEnd ? "sm:justify-end" : "sm:justify-start")}>
|
||||
{controlItems.map((item) => (
|
||||
<div key={item.key} className="flex min-w-0 flex-col gap-1 sm:items-end">
|
||||
<div key={item.key} className={cn("flex min-w-0 flex-col gap-1", alignEnd ? "sm:items-end" : "sm:items-start")}>
|
||||
{controlItems.length > 1 ? (
|
||||
<span className="truncate text-xs text-muted-foreground">{item.title}</span>
|
||||
) : null}
|
||||
@@ -493,6 +505,7 @@ export function WorkspaceRuntimeQuickControls({
|
||||
pendingRequest={pendingRequest}
|
||||
onAction={onAction}
|
||||
square={square}
|
||||
iconOnly={iconOnly}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -503,7 +516,10 @@ export function WorkspaceRuntimeQuickControls({
|
||||
href={serviceUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex min-w-0 items-center gap-1 self-start break-all text-xs text-muted-foreground hover:text-foreground hover:underline sm:self-end"
|
||||
className={cn(
|
||||
"inline-flex min-w-0 items-center gap-1 self-start break-all text-xs text-muted-foreground hover:text-foreground hover:underline",
|
||||
alignEnd ? "sm:self-end" : "sm:self-start",
|
||||
)}
|
||||
>
|
||||
{serviceUrl}
|
||||
<ExternalLink className="h-3.5 w-3.5 shrink-0" />
|
||||
|
||||
Reference in new issue
Block a user