diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index ffadc3fa342f2..d56dc51f66622 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -152,7 +152,7 @@ export const createWorkspace = async ( const user = currentUser(page); await expectUrl(page).toHavePathName(`/@${user.username}/${name}`); - await page.waitForSelector("[data-testid='build-status'] >> text=Running", { + await page.waitForSelector("text=Workspace status: Running", { state: "visible", }); return name; @@ -364,7 +364,7 @@ export const stopWorkspace = async (page: Page, workspaceName: string) => { await page.getByTestId("workspace-stop-button").click(); - await page.waitForSelector("*[data-testid='build-status'] >> text=Stopped", { + await page.waitForSelector("text=Workspace status: Stopped", { state: "visible", }); }; @@ -389,7 +389,7 @@ export const buildWorkspaceWithParameters = async ( await page.getByTestId("confirm-button").click(); } - await page.waitForSelector("*[data-testid='build-status'] >> text=Running", { + await page.waitForSelector("text=Workspace status: Running", { state: "visible", }); }; @@ -412,11 +412,12 @@ export const startAgent = async ( export const downloadCoderVersion = async ( version: string, ): Promise => { - if (version.startsWith("v")) { - version = version.slice(1); + let versionNumber = version; + if (versionNumber.startsWith("v")) { + versionNumber = versionNumber.slice(1); } - const binaryName = `coder-e2e-${version}`; + const binaryName = `coder-e2e-${versionNumber}`; const tempDir = "/tmp/coder-e2e-cache"; // The install script adds `./bin` automatically to the path :shrug: const binaryPath = path.join(tempDir, "bin", binaryName); @@ -438,7 +439,7 @@ export const downloadCoderVersion = async ( path.join(__dirname, "../../install.sh"), [ "--version", - version, + versionNumber, "--method", "standalone", "--prefix", @@ -551,11 +552,8 @@ const emptyPlan = new TextEncoder().encode("{}"); * converts it into an uploadable tar file. */ const createTemplateVersionTar = async ( - responses?: EchoProvisionerResponses, + responses: EchoProvisionerResponses = {}, ): Promise => { - if (!responses) { - responses = {}; - } if (!responses.parse) { responses.parse = [ { @@ -1012,7 +1010,7 @@ export const updateWorkspace = async ( await fillParameters(page, richParameters, buildParameters); await page.getByRole("button", { name: /update parameters/i }).click(); - await page.waitForSelector("*[data-testid='build-status'] >> text=Running", { + await page.waitForSelector("text=Workspace status: Running", { state: "visible", }); }; @@ -1031,7 +1029,7 @@ export const updateWorkspaceParameters = async ( await fillParameters(page, richParameters, buildParameters); await page.getByRole("button", { name: /submit and restart/i }).click(); - await page.waitForSelector("*[data-testid='build-status'] >> text=Running", { + await page.waitForSelector("text=Workspace status: Running", { state: "visible", }); }; diff --git a/site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx b/site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx deleted file mode 100644 index 352153cda65db..0000000000000 --- a/site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { - MockBuildInfo, - MockCanceledWorkspace, - MockCancelingWorkspace, - MockDeletedWorkspace, - MockDeletingWorkspace, - MockFailedWorkspace, - MockPendingWorkspace, - MockStartingWorkspace, - MockStoppedWorkspace, - MockStoppingWorkspace, - MockWorkspace, -} from "testHelpers/entities"; -import { withDashboardProvider } from "testHelpers/storybook"; -import { WorkspaceStatusBadge } from "./WorkspaceStatusBadge"; - -const meta: Meta = { - title: "modules/workspaces/WorkspaceStatusBadge", - component: WorkspaceStatusBadge, - parameters: { - queries: [ - { - key: ["buildInfo"], - data: MockBuildInfo, - }, - ], - }, - decorators: [withDashboardProvider], -}; - -export default meta; -type Story = StoryObj; - -export const Running: Story = { - args: { - workspace: MockWorkspace, - }, -}; - -export const Starting: Story = { - args: { - workspace: MockStartingWorkspace, - }, -}; - -export const Stopped: Story = { - args: { - workspace: MockStoppedWorkspace, - }, -}; - -export const Stopping: Story = { - args: { - workspace: MockStoppingWorkspace, - }, -}; - -export const Deleting: Story = { - args: { - workspace: MockDeletingWorkspace, - }, -}; - -export const Deleted: Story = { - args: { - workspace: MockDeletedWorkspace, - }, -}; - -export const Canceling: Story = { - args: { - workspace: MockCancelingWorkspace, - }, -}; - -export const Canceled: Story = { - args: { - workspace: MockCanceledWorkspace, - }, -}; - -export const Failed: Story = { - args: { - workspace: MockFailedWorkspace, - }, -}; - -export const Pending: Story = { - args: { - workspace: MockPendingWorkspace, - }, -}; diff --git a/site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx b/site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx deleted file mode 100644 index 1bde6f9181ba6..0000000000000 --- a/site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import Tooltip, { - type TooltipProps, - tooltipClasses, -} from "@mui/material/Tooltip"; -import type { Workspace } from "api/typesGenerated"; -import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; -import { Pill } from "components/Pill/Pill"; -import { useClassName } from "hooks/useClassName"; -import { CircleAlertIcon } from "lucide-react"; -import type { FC, ReactNode } from "react"; -import { getDisplayWorkspaceStatus } from "utils/workspace"; - -export type WorkspaceStatusBadgeProps = { - workspace: Workspace; - children?: ReactNode; - className?: string; -}; - -export const WorkspaceStatusBadge: FC = ({ - workspace, - className, -}) => { - const { text, icon, type } = getDisplayWorkspaceStatus( - workspace.latest_build.status, - workspace.latest_build.job, - ); - - return ( - - - - - - - - {text} - - - - ); -}; - -const FailureTooltip: FC = ({ children, ...tooltipProps }) => { - const popper = useClassName( - (css, theme) => css` - & .${tooltipClasses.tooltip} { - background-color: ${theme.palette.background.paper}; - border: 1px solid ${theme.palette.divider}; - font-size: 12px; - padding: 8px 10px; - } - `, - [], - ); - - return ( - - {children} - - ); -}; diff --git a/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.stories.tsx b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.stories.tsx new file mode 100644 index 0000000000000..75205db8ff698 --- /dev/null +++ b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.stories.tsx @@ -0,0 +1,82 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import type { Workspace, WorkspaceStatus } from "api/typesGenerated"; +import { MockWorkspace } from "testHelpers/entities"; +import { WorkspaceStatusIndicator } from "./WorkspaceStatusIndicator"; + +const meta: Meta = { + title: "modules/workspaces/WorkspaceStatusIndicator", + component: WorkspaceStatusIndicator, +}; + +export default meta; +type Story = StoryObj; + +const createWorkspaceWithStatus = (status: WorkspaceStatus): Workspace => { + return { + ...MockWorkspace, + latest_build: { + ...MockWorkspace.latest_build, + status, + }, + } as Workspace; +}; + +export const Running: Story = { + args: { + workspace: createWorkspaceWithStatus("running"), + }, +}; + +export const Stopped: Story = { + args: { + workspace: createWorkspaceWithStatus("stopped"), + }, +}; + +export const Starting: Story = { + args: { + workspace: createWorkspaceWithStatus("starting"), + }, +}; + +export const Stopping: Story = { + args: { + workspace: createWorkspaceWithStatus("stopping"), + }, +}; + +export const Failed: Story = { + args: { + workspace: createWorkspaceWithStatus("failed"), + }, +}; + +export const Canceling: Story = { + args: { + workspace: createWorkspaceWithStatus("canceling"), + }, +}; + +export const Canceled: Story = { + args: { + workspace: createWorkspaceWithStatus("canceled"), + }, +}; + +export const Deleting: Story = { + args: { + workspace: createWorkspaceWithStatus("deleting"), + }, +}; + +export const Deleted: Story = { + args: { + workspace: createWorkspaceWithStatus("deleted"), + }, +}; + +export const Pending: Story = { + args: { + workspace: createWorkspaceWithStatus("pending"), + }, +}; diff --git a/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx new file mode 100644 index 0000000000000..bd928844cdc0f --- /dev/null +++ b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx @@ -0,0 +1,49 @@ +import type { Workspace } from "api/typesGenerated"; +import { + StatusIndicator, + StatusIndicatorDot, + type StatusIndicatorProps, +} from "components/StatusIndicator/StatusIndicator"; +import type { FC } from "react"; +import type React from "react"; +import { + type DisplayWorkspaceStatusType, + getDisplayWorkspaceStatus, +} from "utils/workspace"; + +const variantByStatusType: Record< + DisplayWorkspaceStatusType, + StatusIndicatorProps["variant"] +> = { + active: "pending", + inactive: "inactive", + success: "success", + error: "failed", + danger: "warning", + warning: "warning", +}; + +type WorkspaceStatusIndicatorProps = { + workspace: Workspace; + children?: React.ReactNode; +}; + +export const WorkspaceStatusIndicator: FC = ({ + workspace, + children, +}) => { + const { text, type } = getDisplayWorkspaceStatus( + workspace.latest_build.status, + workspace.latest_build.job, + ); + + return ( + + + + Workspace status: {text} + + {children} + + ); +}; diff --git a/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx b/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx index 32908156c5b5c..8f75e615895f6 100644 --- a/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx @@ -20,7 +20,7 @@ import { Popover, PopoverTrigger } from "components/deprecated/Popover/Popover"; import { TrashIcon } from "lucide-react"; import { useDashboard } from "modules/dashboard/useDashboard"; import { linkToTemplate, useLinks } from "modules/navigation"; -import { WorkspaceStatusBadge } from "modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge"; +import { WorkspaceStatusIndicator } from "modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator"; import type { FC } from "react"; import { useQuery } from "react-query"; import { Link as RouterLink } from "react-router-dom"; @@ -201,18 +201,13 @@ export const WorkspaceTopbar: FC = ({ {!isImmutable && ( -
+
+ = ({ onUpdateWorkspace={handleUpdate} onActivateWorkspace={handleDormantActivate} /> - + + + = { - active: "pending", - inactive: "inactive", - success: "success", - error: "failed", - danger: "warning", - warning: "warning", -}; - const WorkspaceStatusCell: FC = ({ workspace }) => { - const { text, type } = getDisplayWorkspaceStatus( - workspace.latest_build.status, - workspace.latest_build.job, - ); - return (
- - - {text} + {workspace.latest_build.status === "running" && !workspace.health.healthy && ( = ({ workspace }) => { {workspace.dormant_at && ( )} - + {lastUsedMessage(workspace.last_used_at)} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy