mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): reflect submitting state during batch update (#27630)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
## Problem
When bulk updating workspaces, the confirmation modal's **Update**
button never entered a submitting/loading state, so there was no
feedback that the update was actually in progress.
## Root cause
The `BatchUpdateModalForm` shows a spinner when its `isProcessing` prop
is `true`. That prop is fed by `batchActions.isProcessing` from
`useBatchActions`. However, the `isProcessing` value was OR-ing together
every mutation's `isPending` flag **except** `updateAllMutation` — the
one that actually performs the batch update:
```ts
isProcessing:
favoriteAllMutation.isPending ||
unfavoriteAllMutation.isPending ||
startAllMutation.isPending ||
stopAllMutation.isPending ||
deleteAllMutation.isPending,
// updateAllMutation.isPending was missing
```
As a result, the button stayed idle for the entire duration of a bulk
update.
## Fix
Include `updateAllMutation.isPending` in the `isProcessing` derivation
so the button spinner and disabled state correctly reflect an in-flight
batch update.
## Testing
- [ ] Manually verify the Update button shows the spinner and is
disabled while a bulk update runs.
This commit is contained in:
@@ -15,7 +15,11 @@ import {
|
||||
templateVersionsQueryKey,
|
||||
} from "#/api/queries/templates";
|
||||
import { workspacesKey } from "#/api/queries/workspaces";
|
||||
import type { Workspace, WorkspaceAppHealth } from "#/api/typesGenerated";
|
||||
import type {
|
||||
Workspace,
|
||||
WorkspaceAppHealth,
|
||||
WorkspaceBuild,
|
||||
} from "#/api/typesGenerated";
|
||||
import { workspaceChecks } from "#/modules/workspaces/permissions";
|
||||
import {
|
||||
MockDefaultOrganization,
|
||||
@@ -373,6 +377,58 @@ export const BatchUpdateRunningWorkspace: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const BatchUpdateShowsSubmittingState: Story = {
|
||||
beforeEach: () => {
|
||||
spyOn(API, "getWorkspaces").mockResolvedValue({
|
||||
workspaces: updateRunningWorkspaces,
|
||||
count: updateRunningWorkspaces.length,
|
||||
});
|
||||
spyOn(API, "getTemplateVersion").mockResolvedValue(MockTemplateVersion);
|
||||
},
|
||||
play: async ({ canvasElement, step }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const body = within(document.body);
|
||||
const user = userEvent.setup();
|
||||
|
||||
// Hold the mutation pending so the button's submitting state stays
|
||||
// observable until the story explicitly resolves it.
|
||||
let resolveUpdate!: () => void;
|
||||
const pendingUpdate = new Promise<WorkspaceBuild>((resolve) => {
|
||||
resolveUpdate = () => resolve(MockWorkspaceBuild);
|
||||
});
|
||||
spyOn(API, "updateWorkspace").mockReturnValue(pendingUpdate);
|
||||
|
||||
await selectWorkspaces(canvas, user, ["1", "2", "3"]);
|
||||
await openBulkActions(canvas, user);
|
||||
await user.click(await body.findByRole("menuitem", { name: /Update/ }));
|
||||
|
||||
const modal = await body.findByRole("dialog", { name: /Review Updates/i });
|
||||
await user.click(
|
||||
within(modal).getByRole("checkbox", {
|
||||
name: /I acknowledge these risks\./,
|
||||
}),
|
||||
);
|
||||
const updateButton = within(modal).getByRole("button", { name: /Update/ });
|
||||
await user.click(updateButton);
|
||||
|
||||
await step("Button reflects the submitting state", async () => {
|
||||
await waitFor(() => expect(updateButton).toBeDisabled());
|
||||
await within(modal).findByText(
|
||||
"Waiting for workspaces to finish processing",
|
||||
);
|
||||
});
|
||||
|
||||
await step("Resolving the update clears the submitting state", async () => {
|
||||
resolveUpdate();
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
body.queryByText("Waiting for workspaces to finish processing"),
|
||||
).not.toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const ignoreDormantWorkspaces: Workspace[] = [
|
||||
{ ...MockDormantOutdatedWorkspace, id: "1" },
|
||||
{ ...MockOutdatedWorkspace, id: "2" },
|
||||
|
||||
@@ -141,6 +141,7 @@ export function useBatchActions(
|
||||
unfavoriteAllMutation.isPending ||
|
||||
startAllMutation.isPending ||
|
||||
stopAllMutation.isPending ||
|
||||
deleteAllMutation.isPending,
|
||||
deleteAllMutation.isPending ||
|
||||
updateAllMutation.isPending,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user