From f67bb8eb04416dd34969f1dd601a807877ce1a72 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:16:59 -0300 Subject: [PATCH 1/6] Add `force_organization_selection` to environment resource --- .../core/resources/OrganizationSettings.ts | 20 +++++++++++++++++++ .../clerk-js/src/ui/utils/test/fixtures.ts | 1 + packages/types/src/organizationSettings.ts | 2 ++ 3 files changed, 23 insertions(+) diff --git a/packages/clerk-js/src/core/resources/OrganizationSettings.ts b/packages/clerk-js/src/core/resources/OrganizationSettings.ts index ea49f111865..34fa7bd8d65 100644 --- a/packages/clerk-js/src/core/resources/OrganizationSettings.ts +++ b/packages/clerk-js/src/core/resources/OrganizationSettings.ts @@ -20,6 +20,7 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe }; enabled: boolean = false; maxAllowedMemberships: number = 1; + forceOrganizationSelection!: boolean; public constructor(data: OrganizationSettingsJSON | OrganizationSettingsJSONSnapshot | null = null) { super(); @@ -27,6 +28,7 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe } protected fromJSON(data: OrganizationSettingsJSON | OrganizationSettingsJSONSnapshot | null): this { +<<<<<<< HEAD if (!data) { return this; } @@ -44,6 +46,24 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe this.enabled = this.withDefault(data.enabled, this.enabled); this.maxAllowedMemberships = this.withDefault(data.max_allowed_memberships, this.maxAllowedMemberships); +======= + const { + enabled = false, + max_allowed_memberships = 0, + force_organization_selection = false, + actions, + domains, + } = data || {}; + this.enabled = enabled; + this.maxAllowedMemberships = max_allowed_memberships; + this.forceOrganizationSelection = force_organization_selection; + this.actions = { adminDelete: actions?.admin_delete || false }; + this.domains = { + enabled: domains?.enabled || false, + enrollmentModes: domains?.enrollment_modes || [], + defaultRole: domains?.default_role || null, + }; +>>>>>>> 6413d61b9 (Add `force_organization_selection` to environment resource) return this; } diff --git a/packages/clerk-js/src/ui/utils/test/fixtures.ts b/packages/clerk-js/src/ui/utils/test/fixtures.ts index a7cf180205f..9eb97488214 100644 --- a/packages/clerk-js/src/ui/utils/test/fixtures.ts +++ b/packages/clerk-js/src/ui/utils/test/fixtures.ts @@ -82,6 +82,7 @@ const createBaseOrganizationSettings = (): OrganizationSettingsJSON => { return { enabled: false, max_allowed_memberships: 5, + force_organization_selection: false, domains: { enabled: false, enrollment_modes: [], diff --git a/packages/types/src/organizationSettings.ts b/packages/types/src/organizationSettings.ts index 0e5041508c4..afbe09c5c97 100644 --- a/packages/types/src/organizationSettings.ts +++ b/packages/types/src/organizationSettings.ts @@ -8,6 +8,7 @@ export interface OrganizationSettingsJSON extends ClerkResourceJSON { object: never; enabled: boolean; max_allowed_memberships: number; + force_organization_selection: boolean; actions: { admin_delete: boolean; }; @@ -21,6 +22,7 @@ export interface OrganizationSettingsJSON extends ClerkResourceJSON { export interface OrganizationSettingsResource extends ClerkResource { enabled: boolean; maxAllowedMemberships: number; + forceOrganizationSelection: boolean; actions: { adminDelete: boolean; }; From 1937546512fcab9d57fb6fd8de7b7d15fb574710 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:21:37 -0300 Subject: [PATCH 2/6] Hide personal workspace option on components --- .../__tests__/OrganizationList.test.tsx | 18 ++++++++++++++++++ .../__tests__/OrganizationSwitcher.test.tsx | 13 +++++++++++++ .../ui/components/SessionTask/SessionTask.tsx | 8 ++++++-- .../ui/contexts/components/OrganizationList.ts | 4 ++-- .../components/OrganizationSwitcher.ts | 4 ++-- .../src/ui/utils/test/fixtureHelpers.ts | 5 ++++- 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationList/__tests__/OrganizationList.test.tsx b/packages/clerk-js/src/ui/components/OrganizationList/__tests__/OrganizationList.test.tsx index fb58320fce9..f223debb93f 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/__tests__/OrganizationList.test.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/__tests__/OrganizationList.test.tsx @@ -185,6 +185,24 @@ describe('OrganizationList', () => { expect(queryByRole('button', { name: 'Join' })).not.toBeInTheDocument(); }); }); + + describe('with force organization selection setting on environment', () => { + it('does not show the personal account', async () => { + const { wrapper } = await createFixtures(f => { + f.withOrganizations(); + f.withForceOrganizationSelection(); + f.withUser({ + email_addresses: ['test@clerk.com'], + organization_memberships: [{ name: 'Org1', id: '1', role: 'admin' }], + }); + }); + const { queryByText } = render(, { wrapper }); + + await waitFor(() => { + expect(queryByText('Personal account')).not.toBeInTheDocument(); + }); + }); + }); }); describe('CreateOrganization', () => { 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 bac49c52af3..8ec33fee42b 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 @@ -45,6 +45,19 @@ describe('OrganizationSwitcher', () => { expect(queryByText('Personal Workspace')).toBeNull(); expect(getByText('No organization selected')).toBeInTheDocument(); }); + + describe('with force organization selection setting on environment', () => { + it('does not show the personal workspace', async () => { + const { wrapper } = await createFixtures(f => { + f.withOrganizations(); + f.withForceOrganizationSelection(); + f.withUser({ email_addresses: ['test@clerk.com'] }); + }); + const { queryByText, getByRole, userEvent } = render(, { wrapper }); + await userEvent.click(getByRole('button')); + expect(queryByText('Personal Workspace')).toBeNull(); + }); + }); }); describe('OrganizationSwitcherTrigger', () => { diff --git a/packages/clerk-js/src/ui/components/SessionTask/SessionTask.tsx b/packages/clerk-js/src/ui/components/SessionTask/SessionTask.tsx index b869d62265e..dbfed04a19c 100644 --- a/packages/clerk-js/src/ui/components/SessionTask/SessionTask.tsx +++ b/packages/clerk-js/src/ui/components/SessionTask/SessionTask.tsx @@ -7,8 +7,12 @@ import { OrganizationList } from '../OrganizationList'; const ContentRegistry: Record = { org: () => ( - // TODO - Hide personal workspace within organization list context based on environment - + ), diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts index ba86a6e1a4f..0c07c4161f4 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts @@ -11,7 +11,7 @@ export const OrganizationListContext = createContext export const useOrganizationListContext = () => { const context = useContext(OrganizationListContext); const { navigate } = useRouter(); - const { displayConfig } = useEnvironment(); + const { displayConfig, organizationSettings } = useEnvironment(); if (!context || context.componentName !== 'OrganizationList') { throw new Error('Clerk: useOrganizationListContext called outside OrganizationList.'); @@ -80,7 +80,7 @@ export const useOrganizationListContext = () => { afterCreateOrganizationUrl, skipInvitationScreen: ctx.skipInvitationScreen || false, hideSlug: ctx.hideSlug || false, - hidePersonal: ctx.hidePersonal || false, + hidePersonal: organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false, navigateAfterCreateOrganization, navigateAfterSelectOrganization, navigateAfterSelectPersonal, diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts index e4ceffde605..b4afacd7bd5 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts @@ -11,7 +11,7 @@ export const OrganizationSwitcherContext = createContext { const context = useContext(OrganizationSwitcherContext); const { navigate } = useRouter(); - const { displayConfig } = useEnvironment(); + const { displayConfig, organizationSettings } = useEnvironment(); if (!context || context.componentName !== 'OrganizationSwitcher') { throw new Error('Clerk: useOrganizationSwitcherContext called outside OrganizationSwitcher.'); @@ -96,7 +96,7 @@ export const useOrganizationSwitcherContext = () => { return { ...ctx, - hidePersonal: ctx.hidePersonal || false, + hidePersonal: organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false, organizationProfileMode: organizationProfileMode || 'modal', createOrganizationMode: createOrganizationMode || 'modal', skipInvitationScreen: ctx.skipInvitationScreen || false, diff --git a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts index 20d0244c7b2..b7cb2c1ea2b 100644 --- a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts +++ b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts @@ -317,13 +317,16 @@ const createOrganizationSettingsFixtureHelpers = (environment: EnvironmentJSON) const withMaxAllowedMemberships = ({ max = 5 }) => { os.max_allowed_memberships = max; }; + const withForceOrganizationSelection = () => { + os.force_organization_selection = true; + }; const withOrganizationDomains = (modes?: OrganizationEnrollmentMode[], defaultRole?: string) => { os.domains.enabled = true; os.domains.enrollment_modes = modes || ['automatic_invitation', 'automatic_invitation', 'manual_invitation']; os.domains.default_role = defaultRole ?? null; }; - return { withOrganizations, withMaxAllowedMemberships, withOrganizationDomains }; + return { withOrganizations, withMaxAllowedMemberships, withOrganizationDomains, withForceOrganizationSelection }; }; const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => { From 5ce9659f65c823fa544243fe7c141a787cd590a9 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:26:43 -0300 Subject: [PATCH 3/6] Add changeset --- .changeset/fresh-plums-run.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fresh-plums-run.md diff --git a/.changeset/fresh-plums-run.md b/.changeset/fresh-plums-run.md new file mode 100644 index 00000000000..08cc123c4db --- /dev/null +++ b/.changeset/fresh-plums-run.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': patch +'@clerk/types': patch +--- + +Hide personal workspace options when organization selection is enforced From 445d948a83923b610013664038209cc1b55cf1c3 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:35:10 -0300 Subject: [PATCH 4/6] Fix snapshot test --- .../clerk-js/src/core/resources/__tests__/Environment.test.ts | 1 + .../resources/__tests__/__snapshots__/Environment.test.ts.snap | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts index e1f996a7d04..4fcb37f622d 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Environment.test.ts @@ -237,6 +237,7 @@ describe('Environment', () => { organization_settings: { enabled: false, max_allowed_memberships: 5, + force_organization_selection: false, actions: { admin_delete: true }, domains: { enabled: false, enrollment_modes: [], default_role: null }, }, diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap index 1fe9cb48a80..085b8619913 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap @@ -581,6 +581,7 @@ Environment { "enrollmentModes": [], }, "enabled": false, + "forceOrganizationSelection": false, "maxAllowedMemberships": 5, "pathRoot": "", }, From f49b3b0a56e6156b1b41ce96cd50923586e03169 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:12:48 -0300 Subject: [PATCH 5/6] Add telemetry for `hidePersonal` usage vs `force_organization_selection` --- .../src/ui/contexts/components/OrganizationList.ts | 12 ++++++++++++ .../ui/contexts/components/OrganizationSwitcher.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts index 0c07c4161f4..5113b6ac587 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts @@ -1,3 +1,5 @@ +import { useClerk } from '@clerk/shared/react'; +import { eventComponentMounted } from '@clerk/shared/telemetry'; import type { OrganizationResource, UserResource } from '@clerk/types'; import { createContext, useContext } from 'react'; @@ -10,6 +12,7 @@ export const OrganizationListContext = createContext export const useOrganizationListContext = () => { const context = useContext(OrganizationListContext); + const clerk = useClerk(); const { navigate } = useRouter(); const { displayConfig, organizationSettings } = useEnvironment(); @@ -75,6 +78,15 @@ export const useOrganizationListContext = () => { navigateAfterSelectOrganizationOrPersonal({ organization }); const navigateAfterSelectPersonal = (user: UserResource) => navigateAfterSelectOrganizationOrPersonal({ user }); + const hidePersonal = organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false; + + clerk?.telemetry?.record( + eventComponentMounted('OrganizationList', { + hidePersonal, + forceOrganizationSelection: organizationSettings.forceOrganizationSelection, + }), + ); + return { ...ctx, afterCreateOrganizationUrl, diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts index b4afacd7bd5..caf4da189e9 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts @@ -1,3 +1,5 @@ +import { useClerk } from '@clerk/shared/react'; +import { eventComponentMounted } from '@clerk/shared/telemetry'; import type { OrganizationResource, UserResource } from '@clerk/types'; import { createContext, useContext } from 'react'; @@ -10,6 +12,7 @@ export const OrganizationSwitcherContext = createContext { const context = useContext(OrganizationSwitcherContext); + const clerk = useClerk(); const { navigate } = useRouter(); const { displayConfig, organizationSettings } = useEnvironment(); @@ -94,6 +97,15 @@ export const useOrganizationSwitcherContext = () => { const createOrganizationMode = !!ctx.createOrganizationUrl && !ctx.createOrganizationMode ? 'navigation' : ctx.createOrganizationMode; + const hidePersonal = organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false; + + clerk?.telemetry?.record( + eventComponentMounted('OrganizationList', { + hidePersonal, + forceOrganizationSelection: organizationSettings.forceOrganizationSelection, + }), + ); + return { ...ctx, hidePersonal: organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false, From 310307d6ffd628be44d31cd47631bb8e1a0f8b40 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 18 Mar 2025 17:40:57 -0300 Subject: [PATCH 6/6] Fix telemetry to be sent from clerk-js methods --- packages/clerk-js/src/core/clerk.ts | 14 +++++++++-- .../core/resources/OrganizationSettings.ts | 23 ++++--------------- .../contexts/components/OrganizationList.ts | 12 ---------- .../components/OrganizationSwitcher.ts | 12 ---------- 4 files changed, 16 insertions(+), 45 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 58ee4533f35..4960f3f2fc4 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -829,7 +829,12 @@ export class Clerk implements ClerkInterface { }), ); - this.telemetry?.record(eventPrebuiltComponentMounted('OrganizationSwitcher', props)); + this.telemetry?.record( + eventPrebuiltComponentMounted('OrganizationSwitcher', { + ...props, + forceOrganizationSelection: this.environment?.organizationSettings.forceOrganizationSelection, + }), + ); }; public unmountOrganizationSwitcher = (node: HTMLDivElement): void => { @@ -863,7 +868,12 @@ export class Clerk implements ClerkInterface { }), ); - this.telemetry?.record(eventPrebuiltComponentMounted('OrganizationList', props)); + this.telemetry?.record( + eventPrebuiltComponentMounted('OrganizationList', { + ...props, + forceOrganizationSelection: this.environment?.organizationSettings.forceOrganizationSelection, + }), + ); }; public unmountOrganizationList = (node: HTMLDivElement): void => { diff --git a/packages/clerk-js/src/core/resources/OrganizationSettings.ts b/packages/clerk-js/src/core/resources/OrganizationSettings.ts index 34fa7bd8d65..09edfd6b58b 100644 --- a/packages/clerk-js/src/core/resources/OrganizationSettings.ts +++ b/packages/clerk-js/src/core/resources/OrganizationSettings.ts @@ -28,7 +28,6 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe } protected fromJSON(data: OrganizationSettingsJSON | OrganizationSettingsJSONSnapshot | null): this { -<<<<<<< HEAD if (!data) { return this; } @@ -45,25 +44,11 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe this.enabled = this.withDefault(data.enabled, this.enabled); this.maxAllowedMemberships = this.withDefault(data.max_allowed_memberships, this.maxAllowedMemberships); + this.forceOrganizationSelection = this.withDefault( + data.force_organization_selection, + this.forceOrganizationSelection, + ); -======= - const { - enabled = false, - max_allowed_memberships = 0, - force_organization_selection = false, - actions, - domains, - } = data || {}; - this.enabled = enabled; - this.maxAllowedMemberships = max_allowed_memberships; - this.forceOrganizationSelection = force_organization_selection; - this.actions = { adminDelete: actions?.admin_delete || false }; - this.domains = { - enabled: domains?.enabled || false, - enrollmentModes: domains?.enrollment_modes || [], - defaultRole: domains?.default_role || null, - }; ->>>>>>> 6413d61b9 (Add `force_organization_selection` to environment resource) return this; } diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts index 5113b6ac587..0c07c4161f4 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationList.ts @@ -1,5 +1,3 @@ -import { useClerk } from '@clerk/shared/react'; -import { eventComponentMounted } from '@clerk/shared/telemetry'; import type { OrganizationResource, UserResource } from '@clerk/types'; import { createContext, useContext } from 'react'; @@ -12,7 +10,6 @@ export const OrganizationListContext = createContext export const useOrganizationListContext = () => { const context = useContext(OrganizationListContext); - const clerk = useClerk(); const { navigate } = useRouter(); const { displayConfig, organizationSettings } = useEnvironment(); @@ -78,15 +75,6 @@ export const useOrganizationListContext = () => { navigateAfterSelectOrganizationOrPersonal({ organization }); const navigateAfterSelectPersonal = (user: UserResource) => navigateAfterSelectOrganizationOrPersonal({ user }); - const hidePersonal = organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false; - - clerk?.telemetry?.record( - eventComponentMounted('OrganizationList', { - hidePersonal, - forceOrganizationSelection: organizationSettings.forceOrganizationSelection, - }), - ); - return { ...ctx, afterCreateOrganizationUrl, diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts index caf4da189e9..b4afacd7bd5 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationSwitcher.ts @@ -1,5 +1,3 @@ -import { useClerk } from '@clerk/shared/react'; -import { eventComponentMounted } from '@clerk/shared/telemetry'; import type { OrganizationResource, UserResource } from '@clerk/types'; import { createContext, useContext } from 'react'; @@ -12,7 +10,6 @@ export const OrganizationSwitcherContext = createContext { const context = useContext(OrganizationSwitcherContext); - const clerk = useClerk(); const { navigate } = useRouter(); const { displayConfig, organizationSettings } = useEnvironment(); @@ -97,15 +94,6 @@ export const useOrganizationSwitcherContext = () => { const createOrganizationMode = !!ctx.createOrganizationUrl && !ctx.createOrganizationMode ? 'navigation' : ctx.createOrganizationMode; - const hidePersonal = organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false; - - clerk?.telemetry?.record( - eventComponentMounted('OrganizationList', { - hidePersonal, - forceOrganizationSelection: organizationSettings.forceOrganizationSelection, - }), - ); - return { ...ctx, hidePersonal: organizationSettings.forceOrganizationSelection || ctx.hidePersonal || false,