Skip to content

Commit b8fb3db

Browse files
committed
Replace throwing error with opening modal
1 parent ca6f89c commit b8fb3db

File tree

11 files changed

+72
-71
lines changed

11 files changed

+72
-71
lines changed

packages/clerk-js/src/core/clerk.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,8 @@ export class Clerk implements ClerkInterface {
801801
this.assertComponentsReady(this.#componentControls);
802802
if (disabledOrganizationsFeature(this, this.environment)) {
803803
if (this.#instanceType === 'development') {
804-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), {
805-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
804+
this.__internal_openEnableOrganizations({
805+
callerName: 'OrganizationSwitcher',
806806
});
807807
}
808808
return;
@@ -831,8 +831,8 @@ export class Clerk implements ClerkInterface {
831831
this.assertComponentsReady(this.#componentControls);
832832
if (disabledOrganizationsFeature(this, this.environment)) {
833833
if (this.#instanceType === 'development') {
834-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), {
835-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
834+
this.__internal_openEnableOrganizations({
835+
callerName: 'OrganizationSwitcher',
836836
});
837837
}
838838
return;
@@ -973,8 +973,8 @@ export class Clerk implements ClerkInterface {
973973
this.assertComponentsReady(this.#componentControls);
974974
if (disabledOrganizationsFeature(this, this.environment)) {
975975
if (this.#instanceType === 'development') {
976-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), {
977-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
976+
this.__internal_openEnableOrganizations({
977+
callerName: 'OrganizationSwitcher',
978978
});
979979
}
980980
return;
@@ -1013,8 +1013,8 @@ export class Clerk implements ClerkInterface {
10131013
this.assertComponentsReady(this.#componentControls);
10141014
if (disabledOrganizationsFeature(this, this.environment)) {
10151015
if (this.#instanceType === 'development') {
1016-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), {
1017-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1016+
this.__internal_openEnableOrganizations({
1017+
callerName: 'OrganizationSwitcher',
10181018
});
10191019
}
10201020
return;
@@ -1044,8 +1044,8 @@ export class Clerk implements ClerkInterface {
10441044
this.assertComponentsReady(this.#componentControls);
10451045
if (disabledOrganizationsFeature(this, this.environment)) {
10461046
if (this.#instanceType === 'development') {
1047-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationSwitcher'), {
1048-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1047+
this.__internal_openEnableOrganizations({
1048+
callerName: 'OrganizationSwitcher',
10491049
});
10501050
}
10511051
return;
@@ -1083,8 +1083,8 @@ export class Clerk implements ClerkInterface {
10831083
this.assertComponentsReady(this.#componentControls);
10841084
if (disabledOrganizationsFeature(this, this.environment)) {
10851085
if (this.#instanceType === 'development') {
1086-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationList'), {
1087-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1086+
this.__internal_openEnableOrganizations({
1087+
callerName: 'OrganizationSwitcher',
10881088
});
10891089
}
10901090
return;
@@ -1270,8 +1270,8 @@ export class Clerk implements ClerkInterface {
12701270

12711271
if (disabledOrganizationsFeature(this, this.environment)) {
12721272
if (this.#instanceType === 'development') {
1273-
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('TaskChooseOrganization'), {
1274-
code: CANNOT_RENDER_ORGANIZATIONS_DISABLED_ERROR_CODE,
1273+
this.__internal_openEnableOrganizations({
1274+
callerName: 'OrganizationSwitcher',
12751275
});
12761276
}
12771277
return;

packages/clerk-js/src/core/warnings.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,6 @@ const formatWarning = (msg: string) => {
44
return `🔒 Clerk:\n${msg.trim()}\n(This notice only appears in development)`;
55
};
66

7-
const createMessageForDisabledOrganizations = (
8-
componentName:
9-
| 'OrganizationProfile'
10-
| 'OrganizationSwitcher'
11-
| 'OrganizationList'
12-
| 'CreateOrganization'
13-
| 'TaskChooseOrganization',
14-
) => {
15-
return formatWarning(
16-
`The <${componentName}/> cannot be rendered when the feature is turned off. Visit 'dashboard.clerk.com' to enable the feature. Since the feature is turned off, this is no-op.`,
17-
);
18-
};
197
const createMessageForDisabledBilling = (componentName: 'PricingTable' | 'Checkout' | 'PlanDetails') => {
208
return formatWarning(
219
`The <${componentName}/> component cannot be rendered when billing is disabled. Visit 'https://dashboard.clerk.com/last-active?path=billing/settings' to follow the necessary steps to enable billing. Since billing is disabled, this is no-op.`,
@@ -37,7 +25,6 @@ const warnings = {
3725
cannotRenderComponentWhenUserDoesNotExist:
3826
'<UserProfile/> cannot render unless a user is signed in. Since no user is signed in, this is no-op.',
3927
cannotRenderComponentWhenOrgDoesNotExist: `<OrganizationProfile/> cannot render unless an organization is active. Since no organization is currently active, this is no-op.`,
40-
cannotRenderAnyOrganizationComponent: createMessageForDisabledOrganizations,
4128
cannotRenderAnyBillingComponent: createMessageForDisabledBilling,
4229
cannotOpenUserProfile:
4330
'The UserProfile modal cannot render unless a user is signed in. Since no user is signed in, this is no-op.',

packages/clerk-js/src/ui/Components.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,18 @@ const Components = (props: ComponentsProps) => {
352352
};
353353

354354
componentsControls.openModal = (name, props) => {
355+
// Prevent opening enableOrganizations modal if it's already open
356+
// to avoid duplicate mounting when component is called multiple times
357+
if (name === 'enableOrganizations') {
358+
setState(s => {
359+
if (s.enableOrganizationsModal) {
360+
return s; // Modal is already open, don't update state
361+
}
362+
return { ...s, [`${name}Modal`]: props };
363+
});
364+
return;
365+
}
366+
355367
function handleCloseModalForExperimentalUserVerification() {
356368
if (!('afterVerificationCancelled' in props)) {
357369
return;
@@ -496,11 +508,15 @@ const Components = (props: ComponentsProps) => {
496508

497509
const mountedEnableOrganizationsModal = (
498510
<LazyModalRenderer
511+
canCloseModal={false}
499512
globalAppearance={state.appearance}
500513
appearanceKey={'enableOrganizations'}
501514
componentAppearance={enableOrganizationsModal?.appearance}
502515
flowName={'enableOrganizations'}
503-
onClose={() => componentsControls.closeModal('enableOrganizations')}
516+
onClose={() => {
517+
enableOrganizationsModal?.onDismiss?.();
518+
componentsControls.closeModal('enableOrganizations');
519+
}}
504520
onExternalNavigate={() => componentsControls.closeModal('enableOrganizations')}
505521
startPath={buildVirtualRouterUrl({ base: '/enable-organizations', path: urlStateParam?.path })}
506522
componentName={'EnableOrganizationsModal'}

packages/clerk-js/src/ui/components/EnableOrganizations/index.tsx

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,35 @@ import type {
44
} from '@clerk/shared/types';
55
import React from 'react';
66

7-
import { EnableOrganizationsContext } from '@/ui/contexts/components/EnableOrganizations';
7+
import {
8+
EnableOrganizationsContext,
9+
useEnableOrganizationsContext,
10+
} from '@/ui/contexts/components/EnableOrganizations';
811
import { Card } from '@/ui/elements/Card';
912
import { withCardStateProvider } from '@/ui/elements/contexts';
10-
import { Header } from '@/ui/elements/Header';
1113

1214
import { withCoreSessionSwitchGuard } from '../../contexts';
13-
import { Flow } from '../../customizables';
14-
import { Route, Switch } from '../../router';
15+
import { Text } from '../../customizables';
16+
import { Route } from '../../router';
1517

16-
const EnableOrganizationsContent = withCardStateProvider(() => (
17-
<Card.Root>
18-
<Card.Content>
19-
<Header.Root>
20-
<Header.Title>Enable organizations</Header.Title>
21-
</Header.Root>
22-
</Card.Content>
23-
<Card.Footer />
24-
</Card.Root>
25-
));
18+
const EnableOrganizationsContent = withCardStateProvider(() => {
19+
const { callerName } = useEnableOrganizationsContext();
2620

27-
function EnableOrganizationsRoutes(): JSX.Element {
2821
return (
29-
<Flow.Root flow='enableOrganizations'>
30-
<Switch>
31-
<Route index>
32-
<EnableOrganizationsContent />
33-
</Route>
34-
</Switch>
35-
</Flow.Root>
22+
<Card.Root>
23+
<Card.Content>
24+
<Text>
25+
In order to use <code>{callerName}</code>, you need to enable organizations.
26+
</Text>
27+
</Card.Content>
28+
<Card.Footer />
29+
</Card.Root>
3630
);
37-
}
38-
39-
EnableOrganizationsRoutes.displayName = 'EnableOrganizations';
31+
});
4032

4133
const EnableOrganizations: React.ComponentType<__internal_EnableOrganizationsProps> =
42-
withCoreSessionSwitchGuard(EnableOrganizationsRoutes);
34+
withCoreSessionSwitchGuard(EnableOrganizationsContent);
4335

44-
// TODO -> Maybe move this to a inner folder for all in-app modals
4536
const EnableOrganizationsModal = (props: __internal_EnableOrganizationsModalProps): JSX.Element => {
4637
return (
4738
<Route path='enable-organizations'>
@@ -53,7 +44,10 @@ const EnableOrganizationsModal = (props: __internal_EnableOrganizationsModalProp
5344
}}
5445
>
5546
<div>
56-
<EnableOrganizations routing='virtual' />
47+
<EnableOrganizations
48+
routing='virtual'
49+
{...props}
50+
/>
5751
</div>
5852
</EnableOrganizationsContext.Provider>
5953
</Route>

packages/clerk-js/src/ui/contexts/components/EnableOrganizations.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type EnableOrganizationsContextType = EnableOrganizationsCtx;
66

77
export const EnableOrganizationsContext = createContext<EnableOrganizationsCtx | null>(null);
88

9-
export const useEnableOrganizations = (): EnableOrganizationsContextType => {
9+
export const useEnableOrganizationsContext = (): EnableOrganizationsContextType => {
1010
const context = useContext(EnableOrganizationsContext);
1111

1212
if (!context || context.componentName !== 'EnableOrganizations') {

packages/shared/src/organization.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { EnvironmentResource, LoadedClerk, OrganizationMembershipResource } from './types';
1+
import type { LoadedClerk, OrganizationMembershipResource } from './types';
22

33
/**
44
* Finds the organization membership for a given organization ID from a list of memberships
@@ -16,15 +16,6 @@ export function getCurrentOrganizationMembership(
1616
);
1717
}
1818

19-
/**
20-
* Clerk instance with unstable environment access
21-
*
22-
* @internal
23-
*/
24-
export type ClerkWithEnvironment = {
25-
__unstable__environment?: EnvironmentResource | null;
26-
};
27-
2819
/**
2920
* Wraps a hook function in a check to see if organization settings is enabled
3021
*
@@ -36,14 +27,17 @@ export const withOrganizationSettingsEnabled =
3627
<TParams extends any[], TReturn>(
3728
hook: (...args: TParams) => TReturn,
3829
getLoadedClerk: () => LoadedClerk | null | undefined,
30+
callerName?: string,
3931
) =>
4032
(...args: TParams): TReturn => {
4133
const clerk = getLoadedClerk();
4234
// @ts-expect-error - __unstable__environment is not typed
4335
const environment = clerk?.__unstable__environment;
4436

4537
if (!environment?.organizationSettings.enabled) {
46-
clerk?.__internal_openEnableOrganizations({});
38+
clerk?.__internal_openEnableOrganizations({
39+
callerName,
40+
});
4741
}
4842

4943
return hook(...args);

packages/shared/src/react/hooks/useOrganization.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,4 +472,8 @@ function useOrganizationInternal<T extends UseOrganizationParams>(params?: T): U
472472
};
473473
}
474474

475-
export const useOrganization = withOrganizationSettingsEnabled(useOrganizationInternal, () => useClerk());
475+
export const useOrganization = withOrganizationSettingsEnabled(
476+
useOrganizationInternal,
477+
() => useClerk(),
478+
'useOrganization',
479+
);

packages/shared/src/react/hooks/useOrganizationList.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,8 @@ function useOrganizationListInternal<T extends UseOrganizationListParams>(params
386386
};
387387
}
388388

389-
export const useOrganizationList = withOrganizationSettingsEnabled(useOrganizationListInternal, () => useClerk());
389+
export const useOrganizationList = withOrganizationSettingsEnabled(
390+
useOrganizationListInternal,
391+
() => useClerk(),
392+
'useOrganizationList',
393+
);

packages/shared/src/types/appearance.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,6 @@ export type Appearance<T = Theme> = T &
11211121
taskChooseOrganization?: T;
11221122
/**
11231123
* Theme overrides that only apply to the `<EnableOrganizations/>` component
1124-
* TODO -> Change to __internal
11251124
*/
11261125
enableOrganizations?: T;
11271126
};

packages/shared/src/types/clerk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,8 @@ export type __internal_UserVerificationModalProps = WithoutRouting<__internal_Us
14401440

14411441
export type __internal_EnableOrganizationsProps = RoutingOptions & {
14421442
appearance?: EnableOrganizationsTheme;
1443+
callerName?: string;
1444+
onDismiss?: () => void;
14431445
};
14441446

14451447
export type __internal_EnableOrganizationsModalProps = WithoutRouting<__internal_EnableOrganizationsProps>;

0 commit comments

Comments
 (0)