diff --git a/ui/src/context/CompanyContext.test.tsx b/ui/src/context/CompanyContext.test.tsx
index 739fde6ed38..63ac869682e 100644
--- a/ui/src/context/CompanyContext.test.tsx
+++ b/ui/src/context/CompanyContext.test.tsx
@@ -1,114 +1,43 @@
-// @vitest-environment jsdom
+import { describe, expect, it } from "vitest";
+import { resolveBootstrapCompanySelection } from "./CompanyContext";
-import { act, useEffect } from "react";
-import { createRoot, type Root } from "react-dom/client";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
-import { CompanyProvider, useCompany } from "./CompanyContext";
+const activeCompany = { id: "company-1" };
+const archivedCompany = { id: "archived-company" };
-const mockCompaniesApi = vi.hoisted(() => ({
- list: vi.fn(),
- create: vi.fn(),
-}));
-
-vi.mock("../api/companies", () => ({
- companiesApi: mockCompaniesApi,
-}));
-
-function Probe({ onSelectedCompanyId }: { onSelectedCompanyId: (companyId: string | null) => void }) {
- const { selectedCompanyId } = useCompany();
- useEffect(() => {
- onSelectedCompanyId(selectedCompanyId);
- }, [onSelectedCompanyId, selectedCompanyId]);
- return
;
-}
-
-describe("CompanyProvider", () => {
- let container: HTMLDivElement;
- let root: Root;
- let queryClient: QueryClient;
-
- beforeEach(() => {
- (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
- localStorage.clear();
- container = document.createElement("div");
- document.body.appendChild(container);
- root = createRoot(container);
- queryClient = new QueryClient({
- defaultOptions: {
- queries: { retry: false },
- },
- });
+describe("resolveBootstrapCompanySelection", () => {
+ it("does not expose a stale stored company id before companies load", () => {
+ expect(resolveBootstrapCompanySelection({
+ companies: [],
+ sidebarCompanies: [],
+ selectedCompanyId: null,
+ storedCompanyId: "stale-company",
+ })).toBeNull();
});
- afterEach(() => {
- act(() => {
- root.unmount();
- });
- queryClient.clear();
- container.remove();
- vi.clearAllMocks();
+ it("replaces a stale stored company id with the first loaded company", () => {
+ expect(resolveBootstrapCompanySelection({
+ companies: [activeCompany],
+ sidebarCompanies: [activeCompany],
+ selectedCompanyId: null,
+ storedCompanyId: "stale-company",
+ })).toBe("company-1");
});
- it("does not expose a stale stored company id before companies load", async () => {
- localStorage.setItem("paperclip.selectedCompanyId", "stale-company");
- mockCompaniesApi.list.mockImplementation(() => new Promise(() => {}));
- const seen: Array = [];
-
- await act(async () => {
- root.render(
-
-
- seen.push(companyId)} />
-
- ,
- );
- });
-
- expect(seen).toEqual([null]);
+ it("keeps a valid selected company ahead of stored bootstrap state", () => {
+ expect(resolveBootstrapCompanySelection({
+ companies: [activeCompany],
+ sidebarCompanies: [activeCompany],
+ selectedCompanyId: "company-1",
+ storedCompanyId: "stale-company",
+ })).toBe("company-1");
});
- it("replaces a stale stored company id with the first loaded company", async () => {
- localStorage.setItem("paperclip.selectedCompanyId", "stale-company");
- mockCompaniesApi.list.mockResolvedValue([
- {
- id: "company-1",
- name: "Paperclip",
- description: null,
- status: "active",
- issuePrefix: "PAP",
- issueCounter: 1,
- budgetMonthlyCents: 0,
- spentMonthlyCents: 0,
- requireBoardApprovalForNewAgents: false,
- feedbackDataSharingEnabled: false,
- feedbackDataSharingConsentAt: null,
- feedbackDataSharingConsentByUserId: null,
- feedbackDataSharingTermsVersion: null,
- brandColor: null,
- logoAssetId: null,
- logoUrl: null,
- createdAt: new Date(),
- updatedAt: new Date(),
- },
- ]);
- const seen: Array = [];
-
- await act(async () => {
- root.render(
-
-
- seen.push(companyId)} />
-
- ,
- );
- });
-
- await act(async () => {
- await vi.waitFor(() => {
- expect(seen).toEqual([null, "company-1"]);
- expect(localStorage.getItem("paperclip.selectedCompanyId")).toBe("company-1");
- });
- });
+ it("uses selectable sidebar companies before archived companies", () => {
+ expect(resolveBootstrapCompanySelection({
+ companies: [archivedCompany, activeCompany],
+ sidebarCompanies: [activeCompany],
+ selectedCompanyId: null,
+ storedCompanyId: "archived-company",
+ })).toBe("company-1");
});
});
diff --git a/ui/src/context/CompanyContext.tsx b/ui/src/context/CompanyContext.tsx
index 2cb95912b8e..8fc00a36dcd 100644
--- a/ui/src/context/CompanyContext.tsx
+++ b/ui/src/context/CompanyContext.tsx
@@ -35,6 +35,26 @@ const STORAGE_KEY = "paperclip.selectedCompanyId";
const CompanyContext = createContext(null);
+export function resolveBootstrapCompanySelection(input: {
+ companies: Array>;
+ sidebarCompanies: Array>;
+ selectedCompanyId: string | null;
+ storedCompanyId: string | null;
+}) {
+ if (input.companies.length === 0) return null;
+
+ const selectableCompanies = input.sidebarCompanies.length > 0
+ ? input.sidebarCompanies
+ : input.companies;
+ if (input.selectedCompanyId && selectableCompanies.some((company) => company.id === input.selectedCompanyId)) {
+ return input.selectedCompanyId;
+ }
+ if (input.storedCompanyId && selectableCompanies.some((company) => company.id === input.storedCompanyId)) {
+ return input.storedCompanyId;
+ }
+ return selectableCompanies[0]?.id ?? null;
+}
+
export function CompanyProvider({ children }: { children: ReactNode }) {
const queryClient = useQueryClient();
const [selectionSource, setSelectionSource] = useState("bootstrap");
@@ -70,13 +90,13 @@ export function CompanyProvider({ children }: { children: ReactNode }) {
return;
}
- const selectableCompanies = sidebarCompanies.length > 0 ? sidebarCompanies : companies;
- if (selectedCompanyId && selectableCompanies.some((c) => c.id === selectedCompanyId)) return;
-
- const stored = localStorage.getItem(STORAGE_KEY);
- const next = stored && selectableCompanies.some((c) => c.id === stored)
- ? stored
- : selectableCompanies[0]!.id;
+ const next = resolveBootstrapCompanySelection({
+ companies,
+ sidebarCompanies,
+ selectedCompanyId,
+ storedCompanyId: localStorage.getItem(STORAGE_KEY),
+ });
+ if (next === null || next === selectedCompanyId) return;
setSelectedCompanyIdState(next);
setSelectionSource("bootstrap");
localStorage.setItem(STORAGE_KEY, next);