From 4ba56bbf37b1efb01bb507099edb90bb2590f16f Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 13 May 2025 22:24:05 +0000 Subject: [PATCH 1/2] refactor: replace badge by status indicator --- .../WorkspaceStatusBadge.stories.tsx | 93 ------------------- .../WorkspaceStatusBadge.tsx | 90 ------------------ .../WorkspaceStatusIndicator.stories.tsx | 82 ++++++++++++++++ .../WorkspaceStatusIndicator.tsx | 47 ++++++++++ .../pages/WorkspacePage/WorkspaceTopbar.tsx | 15 ++- .../pages/WorkspacesPage/WorkspacesTable.tsx | 35 +------ 6 files changed, 138 insertions(+), 224 deletions(-) delete mode 100644 site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx delete mode 100644 site/src/modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx create mode 100644 site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.stories.tsx create mode 100644 site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx 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..116a517012f4d --- /dev/null +++ b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx @@ -0,0 +1,47 @@ +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 ( + + + {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)} From fe0cb58c9a2954627d489ec324821f4721ad2d32 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 14 May 2025 12:03:38 +0000 Subject: [PATCH 2/2] Improve and fix selectors --- site/e2e/helpers.ts | 24 +++++++++---------- .../WorkspaceStatusIndicator.tsx | 4 +++- 2 files changed, 14 insertions(+), 14 deletions(-) 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/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx index 116a517012f4d..bd928844cdc0f 100644 --- a/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx +++ b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx @@ -40,7 +40,9 @@ export const WorkspaceStatusIndicator: FC = ({ return ( - {text} + + Workspace status: {text} + {children} ); 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