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 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 ea49f111865..09edfd6b58b 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(); @@ -43,6 +44,10 @@ 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, + ); return this; } 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": "", }, 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) => { 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; };