diff --git a/.changeset/friendly-tables-chew.md b/.changeset/friendly-tables-chew.md
new file mode 100644
index 0000000000..2e8c7995aa
--- /dev/null
+++ b/.changeset/friendly-tables-chew.md
@@ -0,0 +1,5 @@
+---
+'@clerk/clerk-js': patch
+---
+
+Use `userMemberships` instead of `organizationList` inside ``.
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/UserMembershipList.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/UserMembershipList.tsx
index e8b69a1ef8..c3aa09dc32 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/UserMembershipList.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/UserMembershipList.tsx
@@ -1,6 +1,7 @@
import type { OrganizationResource } from '@clerk/types';
import React from 'react';
+import { InfiniteListSpinner } from '../../common';
import {
useCoreOrganization,
useCoreOrganizationList,
@@ -9,25 +10,54 @@ import {
} from '../../contexts';
import { Box, descriptors, localizationKeys } from '../../customizables';
import { OrganizationPreview, PersonalWorkspacePreview, PreviewButton } from '../../elements';
+import { useInView } from '../../hooks';
import { SwitchArrows } from '../../icons';
import { common } from '../../styledSystem';
+import { organizationListParams } from './utils';
export type UserMembershipListProps = {
onPersonalWorkspaceClick: React.MouseEventHandler;
onOrganizationClick: (org: OrganizationResource) => unknown;
};
+
+const useFetchMemberships = () => {
+ const { userMemberships } = useCoreOrganizationList({
+ userMemberships: organizationListParams.userMemberships,
+ });
+
+ const { ref } = useInView({
+ threshold: 0,
+ onChange: inView => {
+ if (!inView) {
+ return;
+ }
+ if (userMemberships.hasNextPage) {
+ userMemberships.fetchNext?.();
+ }
+ },
+ });
+
+ return {
+ userMemberships,
+ ref,
+ };
+};
export const UserMembershipList = (props: UserMembershipListProps) => {
const { onPersonalWorkspaceClick, onOrganizationClick } = props;
const { hidePersonal } = useOrganizationSwitcherContext();
const { organization: currentOrg } = useCoreOrganization();
- const { organizationList } = useCoreOrganizationList();
+ const { ref, userMemberships } = useFetchMemberships();
const user = useCoreUser();
- const otherOrgs = (organizationList || []).map(e => e.organization).filter(o => o.id !== currentOrg?.id);
+ const otherOrgs = ((userMemberships.count || 0) > 0 ? userMemberships.data || [] : [])
+ .map(e => e.organization)
+ .filter(o => o.id !== currentOrg?.id);
const { username, primaryEmailAddress, primaryPhoneNumber, ...userWithoutIdentifiers } = user;
+ const { isLoading, hasNextPage } = userMemberships;
+
return (
({
@@ -71,6 +101,7 @@ export const UserMembershipList = (props: UserMembershipListProps) => {
/>
))}
+ {(hasNextPage || isLoading) && }
);
};
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx
index 711ee40fcd..251bf8e0f9 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx
@@ -4,7 +4,11 @@ import { describe } from '@jest/globals';
import { act, render, runFakeTimers, waitFor } from '../../../../testUtils';
import { bindCreateFixtures } from '../../../utils/test/createFixtures';
import { OrganizationSwitcher } from '../OrganizationSwitcher';
-import { createFakeUserOrganizationInvitation, createFakeUserOrganizationSuggestion } from './utlis';
+import {
+ createFakeUserOrganizationInvitation,
+ createFakeUserOrganizationMembership,
+ createFakeUserOrganizationSuggestion,
+} from './utlis';
const { createFixtures } = bindCreateFixtures('OrganizationSwitcher');
@@ -130,11 +134,43 @@ describe('OrganizationSwitcher', () => {
});
it('lists all organizations the user belongs to', async () => {
- const { wrapper, props } = await createFixtures(f => {
+ const { wrapper, props, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withUser({ email_addresses: ['test@clerk.com'], organization_memberships: ['Org1', 'Org2'] });
});
+ fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
+ Promise.resolve({
+ data: [
+ createFakeUserOrganizationMembership({
+ id: '1',
+ organization: {
+ id: '1',
+ name: 'Org1',
+ slug: 'org1',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ createFakeUserOrganizationMembership({
+ id: '2',
+ organization: {
+ id: '2',
+ name: 'Org2',
+ slug: 'org2',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ ],
+ total_count: 2,
+ }),
+ );
+
props.setProps({ hidePersonal: false });
const { getAllByText, getByText, getByRole, userEvent } = render(, { wrapper });
await userEvent.click(getByRole('button'));
@@ -313,6 +349,39 @@ describe('OrganizationSwitcher', () => {
create_organization_enabled: false,
});
});
+
+ fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
+ Promise.resolve({
+ data: [
+ createFakeUserOrganizationMembership({
+ id: '1',
+ organization: {
+ id: '1',
+ name: 'Org1',
+ slug: 'org1',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ createFakeUserOrganizationMembership({
+ id: '2',
+ organization: {
+ id: '2',
+ name: 'Org2',
+ slug: 'org2',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ ],
+ total_count: 2,
+ }),
+ );
+
fixtures.clerk.setActive.mockReturnValueOnce(Promise.resolve());
props.setProps({ hidePersonal: true });
diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserMembershipList.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserMembershipList.tsx
index e37d18a6f5..c3aa09dc32 100644
--- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserMembershipList.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/UserMembershipList.tsx
@@ -1,6 +1,7 @@
import type { OrganizationResource } from '@clerk/types';
import React from 'react';
+import { InfiniteListSpinner } from '../../common';
import {
useCoreOrganization,
useCoreOrganizationList,
@@ -9,26 +10,54 @@ import {
} from '../../contexts';
import { Box, descriptors, localizationKeys } from '../../customizables';
import { OrganizationPreview, PersonalWorkspacePreview, PreviewButton } from '../../elements';
+import { useInView } from '../../hooks';
import { SwitchArrows } from '../../icons';
import { common } from '../../styledSystem';
+import { organizationListParams } from './utils';
export type UserMembershipListProps = {
onPersonalWorkspaceClick: React.MouseEventHandler;
onOrganizationClick: (org: OrganizationResource) => unknown;
};
+
+const useFetchMemberships = () => {
+ const { userMemberships } = useCoreOrganizationList({
+ userMemberships: organizationListParams.userMemberships,
+ });
+
+ const { ref } = useInView({
+ threshold: 0,
+ onChange: inView => {
+ if (!inView) {
+ return;
+ }
+ if (userMemberships.hasNextPage) {
+ userMemberships.fetchNext?.();
+ }
+ },
+ });
+
+ return {
+ userMemberships,
+ ref,
+ };
+};
export const UserMembershipList = (props: UserMembershipListProps) => {
const { onPersonalWorkspaceClick, onOrganizationClick } = props;
const { hidePersonal } = useOrganizationSwitcherContext();
const { organization: currentOrg } = useCoreOrganization();
- const { organizationList } = useCoreOrganizationList();
+ const { ref, userMemberships } = useFetchMemberships();
const user = useCoreUser();
- const otherOrgs = (organizationList || []).map(e => e.organization).filter(o => o.id !== currentOrg?.id);
+ const otherOrgs = ((userMemberships.count || 0) > 0 ? userMemberships.data || [] : [])
+ .map(e => e.organization)
+ .filter(o => o.id !== currentOrg?.id);
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { username, primaryEmailAddress, primaryPhoneNumber, ...userWithoutIdentifiers } = user;
+ const { isLoading, hasNextPage } = userMemberships;
+
return (
({
@@ -72,6 +101,7 @@ export const UserMembershipList = (props: UserMembershipListProps) => {
/>
))}
+ {(hasNextPage || isLoading) && }
);
};
diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx
index 711ee40fcd..251bf8e0f9 100644
--- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/__tests__/OrganizationSwitcher.test.tsx
@@ -4,7 +4,11 @@ import { describe } from '@jest/globals';
import { act, render, runFakeTimers, waitFor } from '../../../../testUtils';
import { bindCreateFixtures } from '../../../utils/test/createFixtures';
import { OrganizationSwitcher } from '../OrganizationSwitcher';
-import { createFakeUserOrganizationInvitation, createFakeUserOrganizationSuggestion } from './utlis';
+import {
+ createFakeUserOrganizationInvitation,
+ createFakeUserOrganizationMembership,
+ createFakeUserOrganizationSuggestion,
+} from './utlis';
const { createFixtures } = bindCreateFixtures('OrganizationSwitcher');
@@ -130,11 +134,43 @@ describe('OrganizationSwitcher', () => {
});
it('lists all organizations the user belongs to', async () => {
- const { wrapper, props } = await createFixtures(f => {
+ const { wrapper, props, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withUser({ email_addresses: ['test@clerk.com'], organization_memberships: ['Org1', 'Org2'] });
});
+ fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
+ Promise.resolve({
+ data: [
+ createFakeUserOrganizationMembership({
+ id: '1',
+ organization: {
+ id: '1',
+ name: 'Org1',
+ slug: 'org1',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ createFakeUserOrganizationMembership({
+ id: '2',
+ organization: {
+ id: '2',
+ name: 'Org2',
+ slug: 'org2',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ ],
+ total_count: 2,
+ }),
+ );
+
props.setProps({ hidePersonal: false });
const { getAllByText, getByText, getByRole, userEvent } = render(, { wrapper });
await userEvent.click(getByRole('button'));
@@ -313,6 +349,39 @@ describe('OrganizationSwitcher', () => {
create_organization_enabled: false,
});
});
+
+ fixtures.clerk.user?.getOrganizationMemberships.mockReturnValueOnce(
+ Promise.resolve({
+ data: [
+ createFakeUserOrganizationMembership({
+ id: '1',
+ organization: {
+ id: '1',
+ name: 'Org1',
+ slug: 'org1',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ createFakeUserOrganizationMembership({
+ id: '2',
+ organization: {
+ id: '2',
+ name: 'Org2',
+ slug: 'org2',
+ membersCount: 1,
+ adminDeleteEnabled: false,
+ maxAllowedMemberships: 1,
+ pendingInvitationsCount: 1,
+ },
+ }),
+ ],
+ total_count: 2,
+ }),
+ );
+
fixtures.clerk.setActive.mockReturnValueOnce(Promise.resolve());
props.setProps({ hidePersonal: true });