Skip to content

Commit 39ca9a1

Browse files
feat(mosaic): open the combined profile straight from Settings behind experimental.newProfiles
1 parent 00c5105 commit 39ca9a1

2 files changed

Lines changed: 29 additions & 10 deletions

File tree

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ function resolveAfterUrl<T extends object>(
140140
return undefined;
141141
}
142142

143+
function opensModal(url: string | undefined, mode: 'navigation' | 'modal' | undefined): boolean {
144+
return (mode ?? (url ? 'navigation' : 'modal')) === 'modal';
145+
}
146+
143147
/** Opens the modal unless a URL routes instead. An explicit mode wins; a URL on its own means navigation. */
144148
function openOrNavigate({
145149
url,
@@ -154,8 +158,7 @@ function openOrNavigate({
154158
buildUrl: () => string;
155159
navigate: (to: string) => unknown;
156160
}): () => void {
157-
const resolved = mode ?? (url ? 'navigation' : 'modal');
158-
return resolved === 'navigation' ? () => void navigate(url ?? buildUrl()) : () => openModal();
161+
return opensModal(url, mode) ? () => openModal() : () => void navigate(url ?? buildUrl());
159162
}
160163

161164
const INVITE_MEMBERS_PERMISSION = 'org:sys_memberships:manage';
@@ -252,6 +255,12 @@ export function useSwitcherModel(options?: SwitcherModelOptions, modals?: Switch
252255
navigate: router.navigate,
253256
});
254257

258+
const managesBothInOneProfile =
259+
newProfiles &&
260+
mosaicUserProfile?.mode === 'combined' &&
261+
opensModal(options?.userProfileUrl, options?.userProfileMode) &&
262+
opensModal(options?.organizationProfileUrl, options?.organizationProfileMode);
263+
255264
const createOrganization = openOrNavigate({
256265
url: options?.createOrganizationUrl,
257266
mode: options?.createOrganizationMode,
@@ -383,7 +392,7 @@ export function useSwitcherModel(options?: SwitcherModelOptions, modals?: Switch
383392
// Single-session apps cannot hold a second account, so both actions are meaningless there.
384393
onSignOutAll: singleSessionMode ? undefined : () => clerk.signOut({ redirectUrl: clerk.buildAfterSignOutUrl() }),
385394
onManageAccount: manageAccount,
386-
onManageOrganization: manageOrganization,
395+
onManageOrganization: managesBothInOneProfile ? undefined : manageOrganization,
387396
// Invite has no page of its own to route to, so it opens its modal even when management is routed.
388397
onInviteMembers: canInviteMembers ? () => clerk.openInviteMembers({ getContainer }) : undefined,
389398
// Covers both restricted instances and users at their creation limit.

‎packages/mosaic/src/features/user-button/__tests__/user-button.new-profiles.feature.test.tsx‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,14 @@ describe('UserButton with experimental.newProfiles', () => {
103103

104104
const profileTabs = () => screen.getAllByRole('tab').map(tab => tab.textContent);
105105

106-
it('opens the combined UserProfile when managing the account, and closes', async () => {
106+
it('opens the combined UserProfile straight from Settings, without a menu, and closes', async () => {
107107
const { clerk, navigate } = await renderWithNewProfiles();
108108
const openUserProfile = vi.spyOn(clerk, 'openUserProfile');
109109
const user = await open();
110110

111-
await openSettings(user, 'Manage profile');
111+
await user.click(screen.getByRole('button', { name: 'Settings' }));
112112

113+
expect(screen.queryByRole('menu')).toBeNull();
113114
expect(await screen.findByRole('tab', { name: 'Account' })).toHaveAttribute('aria-selected', 'true');
114115
expect(profileTabs()).toEqual(['Account', 'Security', 'General', 'Members']);
115116
expect(openUserProfile).not.toHaveBeenCalled();
@@ -149,7 +150,7 @@ describe('UserButton with experimental.newProfiles', () => {
149150
);
150151
const user = await open();
151152

152-
await openSettings(user, 'Manage profile');
153+
await user.click(screen.getByRole('button', { name: 'Settings' }));
153154

154155
expect(within(container).getByRole('tab', { name: 'Account' })).toBeInTheDocument();
155156
});
@@ -163,23 +164,32 @@ describe('UserButton with experimental.newProfiles', () => {
163164
});
164165
const user = await open();
165166

166-
await openSettings(user, 'Manage profile');
167+
await user.click(screen.getByRole('button', { name: 'Settings' }));
167168

168169
expect(await screen.findByRole('tab', { name: 'Terms' })).toHaveAttribute('aria-selected', 'true');
169170
expect(screen.getByText('Terms body')).toBeVisible();
170171
expect(profileTabs()).toEqual(['Terms', 'Account', 'Security', 'General', 'Members']);
171172
});
172173

173-
it("opens the combined profile on the organization's General page, with its own pages in order", async () => {
174+
it("lists the organization's own pages in order in the combined profile", async () => {
174175
await renderWithNewProfiles({ organizationProfileProps: { customPages: [terms], pageOrder: ['terms'] } });
175176
const user = await open();
176177

177-
await openSettings(user, 'Manage organization');
178+
await user.click(screen.getByRole('button', { name: 'Settings' }));
178179

179-
expect(await screen.findByRole('tab', { name: 'General' })).toHaveAttribute('aria-selected', 'true');
180+
expect(await screen.findByRole('tab', { name: 'Account' })).toHaveAttribute('aria-selected', 'true');
180181
expect(profileTabs()).toEqual(['Account', 'Security', 'Terms', 'General', 'Members']);
181182
});
182183

184+
it('still asks which to manage when the organization profile routes to a page', async () => {
185+
const { navigate } = await renderWithNewProfiles({ organizationProfileUrl: '/org' });
186+
const user = await open();
187+
188+
await openSettings(user, 'Manage organization');
189+
190+
expect(navigate).toHaveBeenCalledWith('/org');
191+
});
192+
183193
it("leaves the organization's pages out in user mode", async () => {
184194
await renderWithNewProfiles({ mode: 'user' });
185195
const user = await open();

0 commit comments

Comments
 (0)