Skip to content

Commit 26583a3

Browse files
committed
fix(mosaic): Keep the requested page order when the switcher opens the legacy organization profile
1 parent 71a832d commit 26583a3

4 files changed

Lines changed: 48 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/mosaic': patch
3+
---
4+
5+
Fix the organization profile's page order when `OrganizationSwitcher` or `UserButton` opens the legacy profile for a member who can manage the SSO bypass allowlist. The Security page now stays where `pageOrder` puts it instead of moving to the top.

‎packages/mosaic/src/features/organization-switcher/__tests__/organization-switcher.feature.test.tsx‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,38 @@ describe('OrganizationSwitcher', () => {
305305
expect(reading('Settings', 'Terms of service', 'Support')).toEqual(['Support', 'Terms of service', 'Settings']);
306306
});
307307

308+
describe('the organization profile page order', () => {
309+
const termsPage = { label: 'Terms', path: 'terms', icon: <span />, content: <p>Terms body</p> };
310+
311+
async function openedPageLabels(permissions: string[]) {
312+
const memberships = [fapiMembership(acme, { permissions }), fapiMembership(other)];
313+
const user = fapiUser({ ...alice, organization_memberships: memberships });
314+
const session = fapiSession({ id: 'sess_1', user, last_active_organization_id: 'org_1' });
315+
const { clerk } = await renderSwitcher({}, noOffers({ client: fapiClient([session]), memberships }), {
316+
organizationProfileProps: { customPages: [termsPage], pageOrder: ['terms', 'security'] },
317+
});
318+
const openOrganizationProfile = vi.spyOn(clerk, 'openOrganizationProfile').mockImplementation(() => {});
319+
const clicker = await openWithList();
320+
321+
await clicker.click(screen.getByRole('button', { name: 'Settings' }));
322+
323+
return openOrganizationProfile.mock.calls[0]?.[0]?.customPages?.map(page => page.label);
324+
}
325+
326+
it('keeps Security where it was asked for when the member can manage the SSO bypass allowlist', async () => {
327+
expect(await openedPageLabels(['org:sys_entconns_sso_bypass:manage'])).toEqual([
328+
'Terms',
329+
'security',
330+
'general',
331+
'members',
332+
]);
333+
});
334+
335+
it('leaves Security out when the organization profile will not show it', async () => {
336+
expect(await openedPageLabels([])).toEqual(['Terms', 'general', 'members']);
337+
});
338+
});
339+
308340
it('themes its parts through organization-switcher slots', async () => {
309341
await renderSwitcher({}, noOffers());
310342
await openWithList();

‎packages/mosaic/src/features/switcher/switcher.pages.tsx‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import {
66
disabledUserAPIKeysFeature,
77
disabledUserBillingFeature,
88
} from '@clerk/shared/internal/clerk-js/componentGuards';
9-
import { useClerk } from '@clerk/shared/react';
9+
import { useClerk, useSession } from '@clerk/shared/react';
1010
import type { CustomPage } from '@clerk/shared/types';
1111
import type { ReactNode } from 'react';
1212
import { useCallback, useState } from 'react';
1313
import { createPortal } from 'react-dom';
1414

1515
import { useMosaicEnvironment } from '../../hooks/use-mosaic-environment';
1616
import { applyOrder } from '../../utils/apply-order';
17+
import { ORGANIZATION_PERMISSIONS } from '../../utils/permissions';
1718
import type { OrganizationProfilePageId } from '../organization-profile/organization-profile.types';
1819
import { USER_PROFILE_PAGE_IDS } from '../user-profile/user-profile.layout';
1920
import type { CustomProfilePage, UserProfilePageId } from '../user-profile/user-profile.types';
@@ -42,6 +43,9 @@ export function useUserProfilePages(): UserProfilePageId[] {
4243
export function useOrganizationProfilePages(): OrganizationProfilePageId[] {
4344
const clerk = useClerk();
4445
const environment = useMosaicEnvironment();
46+
const { session } = useSession();
47+
const canManageSSOBypass =
48+
session?.checkAuthorization({ permission: ORGANIZATION_PERMISSIONS.ssoBypassManage }) ?? false;
4549

4650
const pages: OrganizationProfilePageId[] = ['general', 'members'];
4751
if (!disabledOrganizationBillingFeature(clerk, environment)) {
@@ -50,7 +54,11 @@ export function useOrganizationProfilePages(): OrganizationProfilePageId[] {
5054
if (!disabledOrganizationAPIKeysFeature(clerk, environment)) {
5155
pages.push('apiKeys');
5256
}
53-
if (!disabledSelfServeSSOFeature(clerk, environment) || !disabledSelfServeDirectorySyncFeature(clerk, environment)) {
57+
if (
58+
!disabledSelfServeSSOFeature(clerk, environment) ||
59+
!disabledSelfServeDirectorySyncFeature(clerk, environment) ||
60+
canManageSSOBypass
61+
) {
5462
pages.push('security');
5563
}
5664
return pages;

‎packages/mosaic/src/utils/permissions.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export const ORGANIZATION_PERMISSIONS = {
55
membershipsManage: 'org:sys_memberships:manage',
66
profileManage: 'org:sys_profile:manage',
77
profileDelete: 'org:sys_profile:delete',
8+
ssoBypassManage: 'org:sys_entconns_sso_bypass:manage',
89
} as const;

0 commit comments

Comments
 (0)