diff --git a/.changeset/thirty-cooks-cheer.md b/.changeset/thirty-cooks-cheer.md new file mode 100644 index 00000000000..9537a0a571e --- /dev/null +++ b/.changeset/thirty-cooks-cheer.md @@ -0,0 +1,5 @@ +--- +'@clerk/shared': patch +--- + +Fixes a bug where Invitations from `useOrganization` incorrectly depended on options for memberships. diff --git a/packages/clerk-js/src/ui/hooks/__tests__/useCoreOrganization.test.tsx b/packages/clerk-js/src/ui/hooks/__tests__/useCoreOrganization.test.tsx index d862e650719..c5027a3a251 100644 --- a/packages/clerk-js/src/ui/hooks/__tests__/useCoreOrganization.test.tsx +++ b/packages/clerk-js/src/ui/hooks/__tests__/useCoreOrganization.test.tsx @@ -4,6 +4,7 @@ import { describe } from '@jest/globals'; import { act, renderHook, waitFor } from '../../../testUtils'; import { createFakeDomain, + createFakeOrganizationInvitation, createFakeOrganizationMembershipRequest, } from '../../components/OrganizationProfile/__tests__/utils'; import { createFakeUserOrganizationMembership } from '../../components/OrganizationSwitcher/__tests__/utlis'; @@ -16,6 +17,9 @@ const defaultRenderer = () => domains: { pageSize: 2, }, + invitations: { + pageSize: 2, + }, membershipRequests: { pageSize: 2, }, @@ -403,4 +407,204 @@ describe('useOrganization', () => { ); }); }); + + describe('invitations', () => { + it('fetch with pages', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [{ name: 'Org1', role: 'basic_member' }], + }); + }); + + fixtures.clerk.organization?.getInvitations.mockReturnValue( + Promise.resolve({ + data: [ + createFakeOrganizationInvitation({ + id: '1', + emailAddress: 'admin1@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + createFakeOrganizationInvitation({ + id: '2', + emailAddress: 'member2@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + ], + total_count: 4, + }), + ); + const { result } = renderHook(defaultRenderer, { wrapper }); + expect(result.current.invitations?.isLoading).toBe(true); + expect(result.current.invitations?.isFetching).toBe(true); + expect(result.current.invitations?.count).toBe(0); + + await waitFor(() => expect(result.current.invitations?.isLoading).toBe(false)); + + expect(result.current.invitations?.isFetching).toBe(false); + expect(result.current.invitations?.count).toBe(4); + expect(result.current.invitations?.page).toBe(1); + expect(result.current.invitations?.pageCount).toBe(2); + expect(result.current.invitations?.hasNextPage).toBe(true); + + fixtures.clerk.organization?.getInvitations.mockReturnValue( + Promise.resolve({ + data: [ + createFakeOrganizationInvitation({ + id: '3', + emailAddress: 'admin3@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + createFakeOrganizationInvitation({ + id: '4', + emailAddress: 'member4@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + ], + total_count: 4, + }), + ); + + act(() => result.current.invitations?.fetchNext?.()); + + await waitFor(() => expect(result.current.invitations?.isLoading).toBe(true)); + await waitFor(() => expect(result.current.invitations?.isLoading).toBe(false)); + + expect(result.current.invitations?.page).toBe(2); + expect(result.current.invitations?.hasNextPage).toBe(false); + expect(result.current.invitations?.data).toEqual( + expect.arrayContaining([ + expect.not.objectContaining({ + id: '1', + }), + expect.not.objectContaining({ + id: '2', + }), + expect.objectContaining({ + organizationId: '1', + id: '3', + emailAddress: 'admin3@clerk.com', + }), + expect.objectContaining({ + organizationId: '1', + id: '4', + emailAddress: 'member4@clerk.com', + }), + ]), + ); + }); + + it('infinite fetch', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [{ name: 'Org1', role: 'basic_member' }], + }); + }); + + fixtures.clerk.organization?.getInvitations.mockReturnValueOnce( + Promise.resolve({ + data: [ + createFakeOrganizationInvitation({ + id: '1', + emailAddress: 'admin1@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + createFakeOrganizationInvitation({ + id: '2', + emailAddress: 'member2@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + ], + total_count: 4, + }), + ); + const { result } = renderHook( + () => + useOrganization({ + invitations: { + pageSize: 2, + infinite: true, + }, + }), + { wrapper }, + ); + expect(result.current.invitations?.isLoading).toBe(true); + expect(result.current.invitations?.isFetching).toBe(true); + + await waitFor(() => expect(result.current.invitations?.isLoading).toBe(false)); + expect(result.current.invitations?.isFetching).toBe(false); + + fixtures.clerk.organization?.getInvitations.mockReturnValueOnce( + Promise.resolve({ + data: [ + createFakeOrganizationInvitation({ + id: '1', + emailAddress: 'admin1@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + createFakeOrganizationInvitation({ + id: '2', + emailAddress: 'member2@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + ], + total_count: 4, + }), + ); + + fixtures.clerk.organization?.getInvitations.mockReturnValueOnce( + Promise.resolve({ + data: [ + createFakeOrganizationInvitation({ + id: '3', + emailAddress: 'admin3@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + createFakeOrganizationInvitation({ + id: '4', + emailAddress: 'member4@clerk.com', + organizationId: '1', + createdAt: new Date('2022-01-01'), + }), + ], + total_count: 4, + }), + ); + + act(() => result.current.invitations?.fetchNext?.()); + + await waitFor(() => expect(result.current.invitations?.isFetching).toBe(true)); + expect(result.current.invitations?.isLoading).toBe(false); + + await waitFor(() => expect(result.current.invitations?.isFetching).toBe(false)); + expect(result.current.invitations?.data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: '1', + }), + expect.objectContaining({ + id: '2', + }), + expect.objectContaining({ + id: '3', + }), + expect.objectContaining({ + id: '4', + }), + ]), + ); + }); + }); }); diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx index 044f1461859..7e7e4632570 100644 --- a/packages/shared/src/react/hooks/useOrganization.tsx +++ b/packages/shared/src/react/hooks/useOrganization.tsx @@ -246,8 +246,8 @@ export const useOrganization: UseOrganization = params => { }, organization?.getInvitations, { - keepPreviousData: membersSafeValues.keepPreviousData, - infinite: membersSafeValues.infinite, + keepPreviousData: invitationsSafeValues.keepPreviousData, + infinite: invitationsSafeValues.infinite, enabled: !!invitationsParams, }, {