fix(ui): never detach a pending import request from its progress panel

Config edits during an in-flight preview or import previously reset the
mutation to idle, hiding the progress panel while the server request kept
running and detaching its outcome from the UI. Only settled mutation
state is cleared now; a pending request keeps its panel (and the action
buttons stay disabled) until it settles. Component tests exercise config
edits while a deferred mutation is pending in both lanes.
This commit is contained in:
Devin Foley committed 2026-07-30 13:16:52 -07:00
1 parent 2766dcf17a
commit 73afbb42b7
2 files changed
+29 -6

No files matched your search

+21
View File
@@ -346,6 +346,12 @@ describe("CompanyImport", () => {
expect(container.textContent).toContain("Uploading and analyzing your package");
expect(container.textContent).toContain("Keep this page open.");
// Config edits while the request is in flight must not detach it from
// the UI: the progress panel keeps reporting it until it settles.
await enterGithubUrl("https://github.com/acme/starter-b/tree/main/company");
expect(container.textContent).toContain("Uploading and analyzing your package");
await act(async () => {
rejectPreview(new Error("stream disconnected"));
});
@@ -379,6 +385,21 @@ describe("CompanyImport", () => {
expect(container.textContent).toContain("Uploading and importing");
expect(container.textContent).toContain("Keep this page open.");
// Config edits while the import is in flight must not detach it from
// the UI: the progress panel keeps reporting it until it settles.
const midFlightNameInput = container.querySelector<HTMLInputElement>(
'input[placeholder="Imported Company"]',
);
expect(midFlightNameInput).toBeTruthy();
await act(async () => {
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!;
setter.call(midFlightNameInput!, "Mid Flight");
midFlightNameInput!.dispatchEvent(new Event("input", { bubbles: true }));
});
await flushReact();
expect(container.textContent).toContain("Uploading and importing");
await act(async () => {
rejectImport(new Error("connection reset"));
});
+8 -6
View File
@@ -927,13 +927,15 @@ export function CompanyImport() {
},
});
// Any change to the import configuration supersedes the request the last
// progress/error panel describes, so it resets both mutations. The
// new-company name is typed against a rendered preview, so it resets only
// the panels; structural changes also discard the preview itself.
// Any change to the import configuration supersedes the request a settled
// progress/error panel describes, so it clears settled mutation state. A
// pending request is never detached: its panel keeps reporting it (and the
// action buttons stay disabled) until it settles. The new-company name is
// typed against a rendered preview, so it resets only the panels;
// structural changes also discard the preview itself.
function resetMutationState() {
previewMutation.reset();
importMutation.reset();
if (!previewMutation.isPending) previewMutation.reset();
if (!importMutation.isPending) importMutation.reset();
}
function resetImportFlowState() {