diff --git a/site/src/components/DropdownButton/ActionCtas.tsx b/site/src/components/DropdownButton/ActionCtas.tsx index 7f104da3833dd..0186797a9703d 100644 --- a/site/src/components/DropdownButton/ActionCtas.tsx +++ b/site/src/components/DropdownButton/ActionCtas.tsx @@ -160,7 +160,6 @@ const useStyles = makeStyles((theme) => ({ // this is all custom to work with our button wrapper loadingButton: { border: "none", - borderLeft: "1px solid #333740", // MUI disabled button - borderRadius: "3px 0px 0px 3px", + borderRadius: `${theme.shape.borderRadius} 0px 0px ${theme.shape.borderRadius}`, }, })) diff --git a/site/src/components/DropdownButton/DropdownButton.tsx b/site/src/components/DropdownButton/DropdownButton.tsx index f7ffcdbb187b7..c4067f9293ecc 100644 --- a/site/src/components/DropdownButton/DropdownButton.tsx +++ b/site/src/components/DropdownButton/DropdownButton.tsx @@ -95,7 +95,7 @@ const useStyles = makeStyles((theme) => ({ borderLeft: `1px solid ${theme.palette.divider}`, borderRadius: `0px ${theme.shape.borderRadius}px ${theme.shape.borderRadius}px 0px`, minWidth: "unset", - width: "63px", // matching cancel button so button grouping doesn't grow in size + width: "64px", // matching cancel button so button grouping doesn't grow in size "& .MuiButton-label": { marginRight: "8px", }, diff --git a/site/src/components/LoadingButton/LoadingButton.tsx b/site/src/components/LoadingButton/LoadingButton.tsx index 6efb4e4aa7f7e..60c0d4997bc31 100644 --- a/site/src/components/LoadingButton/LoadingButton.tsx +++ b/site/src/components/LoadingButton/LoadingButton.tsx @@ -31,7 +31,7 @@ export const LoadingButton: FC> = ({ {children} {loading && (
- +
)} {Boolean(loadingLabel) && loadingLabel} @@ -63,7 +63,7 @@ const useStyles = makeStyles((theme) => ({ top: "50%", left: "50%", height: 22, // centering loading icon - width: 18, + width: 16, }, spinner: { color: theme.palette.text.disabled, diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.tsx index 66b6a18d9278a..503ae6e7947d6 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.tsx @@ -64,7 +64,7 @@ export const WorkspaceActions: FC = ({ ), [ButtonTypesEnum.pending]: ( - + ), } diff --git a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx index bacd99e967542..c05090d02622f 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx @@ -92,16 +92,6 @@ afterAll(() => { }) describe("WorkspacePage", () => { - it("requests a stop job when the user presses Stop", async () => { - const stopWorkspaceMock = jest - .spyOn(api, "stopWorkspace") - .mockResolvedValueOnce(MockWorkspaceBuild) - testButton( - t("actionButton.stop", { ns: "workspacePage" }), - stopWorkspaceMock, - ) - }) - it("requests a delete job when the user presses Delete and confirms", async () => { const user = userEvent.setup() const deleteWorkspaceMock = jest @@ -140,11 +130,23 @@ describe("WorkspacePage", () => { const startWorkspaceMock = jest .spyOn(api, "startWorkspace") .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) - testButton( + await testButton( t("actionButton.start", { ns: "workspacePage" }), startWorkspaceMock, ) }) + + it("requests a stop job when the user presses Stop", async () => { + const stopWorkspaceMock = jest + .spyOn(api, "stopWorkspace") + .mockResolvedValueOnce(MockWorkspaceBuild) + + await testButton( + t("actionButton.stop", { ns: "workspacePage" }), + stopWorkspaceMock, + ) + }) + it("requests cancellation when the user presses Cancel", async () => { server.use( rest.get( diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index 83111dc2a0a9f..13b8afa1b8b77 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -325,7 +325,7 @@ export const workspaceMachine = createMachine( }, }, requestingStart: { - entry: "clearBuildError", + entry: ["clearBuildError", "updateStatusToPending"], invoke: { src: "startWorkspace", id: "startWorkspace", @@ -344,7 +344,7 @@ export const workspaceMachine = createMachine( }, }, requestingStop: { - entry: "clearBuildError", + entry: ["clearBuildError", "updateStatusToPending"], invoke: { src: "stopWorkspace", id: "stopWorkspace", @@ -363,7 +363,7 @@ export const workspaceMachine = createMachine( }, }, requestingDelete: { - entry: "clearBuildError", + entry: ["clearBuildError", "updateStatusToPending"], invoke: { src: "deleteWorkspace", id: "deleteWorkspace", @@ -382,7 +382,11 @@ export const workspaceMachine = createMachine( }, }, requestingCancel: { - entry: ["clearCancellationMessage", "clearCancellationError"], + entry: [ + "clearCancellationMessage", + "clearCancellationError", + "updateStatusToPending", + ], invoke: { src: "cancelWorkspace", id: "cancelWorkspace", @@ -430,9 +434,7 @@ export const workspaceMachine = createMachine( on: { REFRESH_TIMELINE: { target: "#workspaceState.ready.timeline.gettingBuilds", - cond: { - type: "moreBuildsAvailable", - }, + cond: "moreBuildsAvailable", }, }, }, @@ -599,6 +601,24 @@ export const workspaceMachine = createMachine( }), { to: "scheduleBannerMachine" }, ), + // Optimistically update. So when the user clicks on stop, we can show + // the "pending" state right away without having to wait 0.5s ~ 2s to + // display the visual feedback to the user. + updateStatusToPending: assign({ + workspace: ({ workspace }) => { + if (!workspace) { + throw new Error("Workspace not defined") + } + + return { + ...workspace, + latest_build: { + ...workspace.latest_build, + status: "pending" as TypesGen.WorkspaceStatus, + }, + } + }, + }), }, guards: { moreBuildsAvailable,