diff --git a/.changeset/chatty-boats-tease.md b/.changeset/chatty-boats-tease.md
new file mode 100644
index 0000000000..b8ce56e719
--- /dev/null
+++ b/.changeset/chatty-boats-tease.md
@@ -0,0 +1,6 @@
+---
+'@clerk/clerk-js': patch
+'@clerk/shared': patch
+---
+
+Fix incorrect pagination counters in data tables inside ``.
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx
index ec45520e67..ea1417821b 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/ActiveMembersList.tsx
@@ -8,11 +8,16 @@ import { useFetchRoles, useLocalizeCustomRoles } from '../../hooks/useFetchRoles
import { handleError } from '../../utils';
import { DataTable, RoleSelect, RowContainer } from './MemberListTable';
+const membershipsParams = {
+ memberships: {
+ pageSize: 10,
+ keepPreviousData: true,
+ },
+};
+
export const ActiveMembersList = () => {
const card = useCardState();
- const { organization, memberships } = useCoreOrganization({
- memberships: true,
- });
+ const { organization, memberships } = useCoreOrganization(membershipsParams);
const { options, isLoading: loadingRoles } = useFetchRoles();
@@ -44,6 +49,7 @@ export const ActiveMembersList = () => {
onPageChange={n => memberships?.fetchPage?.(n)}
itemCount={memberships?.count || 0}
pageCount={memberships?.pageCount || 0}
+ itemsPerPage={membershipsParams.memberships.pageSize}
isLoading={memberships?.isLoading || loadingRoles}
emptyStateLocalizationKey={localizationKeys('organizationProfile.membersPage.detailsTitle__emptyRow')}
headers={[
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx
index f6a96ec4d7..504bbf0431 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/InvitedMembersList.tsx
@@ -7,11 +7,16 @@ import { useLocalizeCustomRoles } from '../../hooks/useFetchRoles';
import { handleError } from '../../utils';
import { DataTable, RowContainer } from './MemberListTable';
+const invitationsParams = {
+ invitations: {
+ pageSize: 10,
+ keepPreviousData: true,
+ },
+};
+
export const InvitedMembersList = () => {
const card = useCardState();
- const { organization, invitations } = useCoreOrganization({
- invitations: true,
- });
+ const { organization, invitations } = useCoreOrganization(invitationsParams);
if (!organization) {
return null;
@@ -32,6 +37,7 @@ export const InvitedMembersList = () => {
onPageChange={invitations?.fetchPage || (() => null)}
itemCount={invitations?.count || 0}
pageCount={invitations?.pageCount || 0}
+ itemsPerPage={invitationsParams.invitations.pageSize}
isLoading={invitations?.isLoading}
emptyStateLocalizationKey={localizationKeys('organizationProfile.membersPage.invitationsTab.table__emptyRow')}
headers={[
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/MemberListTable.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/MemberListTable.tsx
index 60187d8493..efdd2d5207 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/MemberListTable.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/MemberListTable.tsx
@@ -15,16 +15,9 @@ type MembersListTableProps = {
onPageChange: (page: number) => void;
itemCount: number;
emptyStateLocalizationKey: LocalizationKey;
-} & (
- | {
- itemsPerPage?: never;
- pageCount: number;
- }
- | {
- itemsPerPage: number;
- pageCount?: never;
- }
-);
+ pageCount: number;
+ itemsPerPage: number;
+};
export const DataTable = (props: MembersListTableProps) => {
const {
@@ -35,13 +28,12 @@ export const DataTable = (props: MembersListTableProps) => {
isLoading,
itemCount,
itemsPerPage,
- pageCount: pageCountProp,
+ pageCount,
emptyStateLocalizationKey,
} = props;
- const pageCount = rows.length !== 0 ? pageCountProp ?? Math.ceil(itemCount / itemsPerPage) : 1;
- const startRowIndex = (page - 1) * rows.length;
- const endRowIndex = Math.min(page * rows.length);
+ const startingRow = itemCount > 0 ? Math.max(0, (page - 1) * itemsPerPage) + 1 : 0;
+ const endingRow = Math.min(page * itemsPerPage, itemCount);
return (
{
siblingCount={1}
rowInfo={{
allRowsCount: itemCount,
- startingRow: rows.length ? startRowIndex + 1 : startRowIndex,
- endingRow: endRowIndex,
+ startingRow,
+ endingRow,
}}
/>
}
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx
index e494b8f4a3..709cbd7043 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/RequestToJoinList.tsx
@@ -6,16 +6,16 @@ import { useCardState, UserPreview, withCardStateProvider } from '../../elements
import { handleError } from '../../utils';
import { DataTable, RowContainer } from './MemberListTable';
-const ITEMS_PER_PAGE = 10;
-
const membershipRequestsParams = {
- pageSize: ITEMS_PER_PAGE,
+ membershipRequests: {
+ pageSize: 10,
+ keepPreviousData: true,
+ },
};
+
export const RequestToJoinList = () => {
const card = useCardState();
- const { organization, membershipRequests } = useCoreOrganization({
- membershipRequests: membershipRequestsParams,
- });
+ const { organization, membershipRequests } = useCoreOrganization(membershipRequestsParams);
if (!organization) {
return null;
@@ -25,8 +25,9 @@ export const RequestToJoinList = () => {
null)}
- itemCount={membershipRequests?.count ?? 0}
- itemsPerPage={ITEMS_PER_PAGE}
+ itemCount={membershipRequests?.count || 0}
+ pageCount={membershipRequests?.pageCount || 0}
+ itemsPerPage={membershipRequestsParams.membershipRequests.pageSize}
isLoading={membershipRequests?.isLoading}
emptyStateLocalizationKey={localizationKeys('organizationProfile.membersPage.requestsTab.table__emptyRow')}
headers={[
@@ -49,9 +50,7 @@ const RequestRow = withCardStateProvider(
(props: { request: OrganizationMembershipRequestResource; onError: ReturnType['setError'] }) => {
const { request, onError } = props;
const card = useCardState();
- const { membership, membershipRequests } = useCoreOrganization({
- membershipRequests: membershipRequestsParams,
- });
+ const { membership, membershipRequests } = useCoreOrganization(membershipRequestsParams);
const onAccept = () => {
if (!membership || !membershipRequests) {
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationMembers.test.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationMembers.test.tsx
index 7309ab1b94..21cf5e1cc2 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationMembers.test.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/__tests__/OrganizationMembers.test.tsx
@@ -172,6 +172,76 @@ describe('OrganizationMembers', () => {
});
});
+ it('display pagination counts for 2 pages', async () => {
+ const { wrapper, fixtures } = await createFixtures(f => {
+ f.withOrganizations();
+ f.withUser({
+ email_addresses: ['test@clerk.com'],
+ organization_memberships: [{ name: 'Org1', id: '1' }],
+ });
+ });
+
+ fixtures.clerk.organization?.getMemberships.mockReturnValueOnce(
+ Promise.resolve({
+ data: [],
+ total_count: 14,
+ }),
+ );
+
+ fixtures.clerk.organization?.getRoles.mockRejectedValue(null);
+
+ const { queryByText, getByText } = render(, { wrapper });
+
+ await waitFor(async () => {
+ expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
+ expect(fixtures.clerk.organization?.getInvitations).not.toHaveBeenCalled();
+ expect(fixtures.clerk.organization?.getMembershipRequests).not.toHaveBeenCalled();
+
+ expect(queryByText(/displaying/i)).toBeInTheDocument();
+
+ expect(queryByText(/1 – 10/i)).toBeInTheDocument();
+ expect(queryByText(/of/i)).toBeInTheDocument();
+ expect(queryByText(/^14/i)).toBeInTheDocument();
+ });
+
+ await act(async () => await userEvent.click(getByText(/next/i)));
+
+ await waitFor(async () => {
+ expect(queryByText(/11 – 14/i)).toBeInTheDocument();
+ expect(queryByText(/of/i)).toBeInTheDocument();
+ expect(queryByText(/^14/i)).toBeInTheDocument();
+ });
+ });
+
+ it('display pagination counts for 1 page', async () => {
+ const { wrapper, fixtures } = await createFixtures(f => {
+ f.withOrganizations();
+ f.withUser({
+ email_addresses: ['test@clerk.com'],
+ organization_memberships: [{ name: 'Org1', id: '1' }],
+ });
+ });
+
+ fixtures.clerk.organization?.getMemberships.mockReturnValueOnce(
+ Promise.resolve({
+ data: [],
+ total_count: 5,
+ }),
+ );
+
+ fixtures.clerk.organization?.getRoles.mockRejectedValue(null);
+
+ const { queryByText, getByText } = render(, { wrapper });
+
+ await waitFor(async () => {
+ expect(queryByText(/displaying/i)).toBeInTheDocument();
+ expect(queryByText(/1 – 5/i)).toBeInTheDocument();
+ expect(queryByText(/of/i)).toBeInTheDocument();
+ expect(queryByText(/^5/i)).toBeInTheDocument();
+ expect(getByText(/next/i)).toBeDisabled();
+ });
+ });
+
// TODO: Bring this test back once we can determine the last admin by permissions.
it.skip('Last admin cannot change to member', async () => {
const membersList: OrganizationMembershipResource[] = [
diff --git a/packages/shared/src/react/hooks/useOrganization.tsx b/packages/shared/src/react/hooks/useOrganization.tsx
index a104d240d6..15a6f776bd 100644
--- a/packages/shared/src/react/hooks/useOrganization.tsx
+++ b/packages/shared/src/react/hooks/useOrganization.tsx
@@ -1,9 +1,11 @@
import type {
+ ClerkPaginatedResponse,
ClerkPaginationParams,
GetDomainsParams,
GetInvitationsParams,
GetMembershipRequestParams,
GetMembershipsParams,
+ GetMembersParams,
GetPendingInvitationsParams,
OrganizationDomainResource,
OrganizationInvitationResource,
@@ -11,8 +13,6 @@ import type {
OrganizationMembershipResource,
OrganizationResource,
} from '@clerk/types';
-import type { ClerkPaginatedResponse } from '@clerk/types';
-import type { GetMembersParams } from '@clerk/types';
import { deprecated } from '../../deprecated';
import { useSWR } from '../clerk-swr';