fix(boards): open tabs before async fetch in bulk action
Pre-open blank tabs synchronously to preserve browser user-activation, then navigate those tab handles after app URLs are fetched. Also close surplus tabs and update tests for the new flow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
b97856dcb8
commit
5d9bfa3978
2 files changed
+27
-9
No files matched your search
@@ -19,13 +19,23 @@ export const openAppsInNewTabs = async (
|
||||
if (appIds.length === 0) return;
|
||||
|
||||
const distinctAppIds = [...new Set(appIds)];
|
||||
const openedWindows = distinctAppIds.map(() => window.open("", "_blank", "noopener,noreferrer"));
|
||||
for (const openedWindow of openedWindows) {
|
||||
if (!openedWindow) continue;
|
||||
openedWindow.opener = null;
|
||||
}
|
||||
|
||||
const apps = await fetchAppsByIds(distinctAppIds);
|
||||
const appsWithUrls = apps.filter((app) => app.href && app.href.length > 0);
|
||||
const tabsToClose = openedWindows.slice(appsWithUrls.length).filter((openedWindow): openedWindow is Window => Boolean(openedWindow));
|
||||
for (const tabToClose of tabsToClose) {
|
||||
tabToClose.close();
|
||||
}
|
||||
|
||||
for (const app of appsWithUrls) {
|
||||
const openedWindow = window.open(app.href ?? undefined, "_blank", "noopener,noreferrer");
|
||||
for (const [index, app] of appsWithUrls.entries()) {
|
||||
const openedWindow = openedWindows[index];
|
||||
if (openedWindow) {
|
||||
openedWindow.opener = null;
|
||||
openedWindow.location.href = app.href;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ import { describe, expect, test, vi } from "vitest";
|
||||
import { openAppsInNewTabs } from "../open-apps-in-new-tabs";
|
||||
|
||||
describe("openAppsInNewTabs", () => {
|
||||
test("opens fetched apps with URLs in new tabs with noopener", async () => {
|
||||
const open = vi.spyOn(window, "open").mockReturnValue({ opener: null } as unknown as Window);
|
||||
test("opens tabs synchronously and navigates them to fetched URLs", async () => {
|
||||
const openedWindowOne = { opener: null, location: { href: "" }, close: vi.fn() } as unknown as Window;
|
||||
const openedWindowTwo = { opener: null, location: { href: "" }, close: vi.fn() } as unknown as Window;
|
||||
const openedWindowThree = { opener: null, location: { href: "" }, close: vi.fn() } as unknown as Window;
|
||||
const open = vi.spyOn(window, "open").mockReturnValueOnce(openedWindowOne).mockReturnValueOnce(openedWindowTwo).mockReturnValueOnce(openedWindowThree);
|
||||
const fetchAppsByIds = vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ href: "https://example.com/one" }, { href: null }, { href: "https://example.com/two" }]);
|
||||
@@ -17,14 +20,19 @@ describe("openAppsInNewTabs", () => {
|
||||
});
|
||||
|
||||
expect(fetchAppsByIds).toHaveBeenCalledWith(["app-one", "app-two", "app-three"]);
|
||||
expect(open).toHaveBeenCalledTimes(2);
|
||||
expect(open).toHaveBeenNthCalledWith(1, "https://example.com/one", "_blank", "noopener,noreferrer");
|
||||
expect(open).toHaveBeenNthCalledWith(2, "https://example.com/two", "_blank", "noopener,noreferrer");
|
||||
expect(open).toHaveBeenCalledTimes(3);
|
||||
expect(open).toHaveBeenNthCalledWith(1, "", "_blank", "noopener,noreferrer");
|
||||
expect(open).toHaveBeenNthCalledWith(2, "", "_blank", "noopener,noreferrer");
|
||||
expect(open).toHaveBeenNthCalledWith(3, "", "_blank", "noopener,noreferrer");
|
||||
expect(openedWindowOne.location.href).toBe("https://example.com/one");
|
||||
expect(openedWindowTwo.location.href).toBe("https://example.com/two");
|
||||
expect(openedWindowThree.close).toHaveBeenCalledTimes(1);
|
||||
expect(openConfirmModal).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("de-duplicates app IDs before fetching", async () => {
|
||||
const open = vi.spyOn(window, "open").mockReturnValue({ opener: null } as unknown as Window);
|
||||
const openedWindow = { opener: null, location: { href: "" }, close: vi.fn() } as unknown as Window;
|
||||
const open = vi.spyOn(window, "open").mockReturnValue(openedWindow);
|
||||
const fetchAppsByIds = vi.fn().mockResolvedValue([{ href: "https://example.com/one" }]);
|
||||
const openConfirmModal = vi.fn();
|
||||
|
||||
|
||||
Reference in new issue
Block a user