Skip to content

Commit

Permalink
refactor(clerk-react,types): Resolve PR comments
Browse files Browse the repository at this point in the history
  • Loading branch information
anagstef committed Oct 24, 2023
1 parent 579ab9a commit 4331688
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 26 deletions.
34 changes: 33 additions & 1 deletion .changeset/proud-ways-lie.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,36 @@
'@clerk/types': minor
---

Introduce Custom Pages in UserProfile and OrganizationProfile
Introduce customization in `UserProfile` and `OrganizationProfile`

The `<UserProfile />` component now allows the addition of custom pages and external links to the navigation sidebar. Custom pages can be created using the `<UserProfile.Page>` component, and external links can be added using the `<UserProfile.Link>` component. The default routes, such as `Account` and `Security`, can be reordered.

Example React API usage:

```tsx
<UserProfile>
<UserProfile.Page label="Custom Page" url="custom" labelIcon={<CustomIcon />}>
<MyCustomPageContent />
</UserProfile.Page>
<UserProfile.Link label="External" url="/home" labelIcon={<Icon />} />
<UserProfile.Page label="account" />
<UserProfile.Page label="security" />
</UserProfile>
```
Custom pages and links should be provided as children using the `<UserButton.UserProfilePage>` and `<UserButton.UserProfileLink>` components when using the `UserButton` component.

The `<OrganizationProfile />` component now supports the addition of custom pages and external links to the navigation sidebar. Custom pages can be created using the `<OrganizationProfile.Page>` component, and external links can be added using the `<OrganizationProfile.Link>` component. The default routes, such as `Members` and `Settings`, can be reordered.

Example React API usage:

```tsx
<OrganizationProfile>
<OrganizationProfile.Page label="Custom Page" url="custom" labelIcon={<CustomIcon />}>
<MyCustomPageContent />
</OrganizationProfile.Page>
<OrganizationProfile.Link label="External" url="/home" labelIcon={<Icon />} />
<OrganizationProfile.Page label="members" />
<OrganizationProfile.Page label="settings" />
</OrganizationProfile>
```
Custom pages and links should be provided as children using the `<OrganizationSwitcher.OrganizationProfilePage>` and `<OrganizationSwitcher.OrganizationProfileLink>` components when using the `OrganizationSwitcher` component.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CustomPageContentContainer } from '../../common/CustomPageContentContainer';
import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID } from '../../constants';
import { Gate } from '../../common/Gate';
import { useOrganizationProfileContext } from '../../contexts';
import { ProfileCardContent } from '../../elements';
Expand All @@ -16,8 +17,8 @@ import { VerifyDomainPage } from './VerifyDomainPage';

export const OrganizationProfileRoutes = (props: PropsOfComponent<typeof ProfileCardContent>) => {
const { pages } = useOrganizationProfileContext();
const isMembersPageRoot = pages.routes[0].id === 'members';
const isSettingsPageRoot = pages.routes[0].id === 'settings';
const isMembersPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.MEMBERS;
const isSettingsPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SETTINGS;

const customPageRoutesWithContents = pages.contents?.map((customPage, index) => {
const shouldFirstCustomItemBeOnRoot = !isSettingsPageRoot && !isMembersPageRoot && index === 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CustomPageContentContainer } from '../../common/CustomPageContentContainer';
import { USER_PROFILE_NAVBAR_ROUTE_ID } from '../../constants';
import { useUserProfileContext } from '../../contexts';
import { ProfileCardContent } from '../../elements';
import { Route, Switch } from '../../router';
Expand All @@ -25,7 +26,9 @@ import { Web3Page } from './Web3Page';

export const UserProfileRoutes = (props: PropsOfComponent<typeof ProfileCardContent>) => {
const { pages } = useUserProfileContext();
const isAccountPageRoot = pages.routes[0].id === 'account' || pages.routes[0].id === 'security';
const isAccountPageRoot =
pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT ||
pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.SECURITY;

const customPageRoutesWithContents = pages.contents?.map((customPage, index) => {
const shouldFirstCustomItemBeOnRoot = !isAccountPageRoot && index === 0;
Expand Down
9 changes: 9 additions & 0 deletions packages/clerk-js/src/ui/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const USER_PROFILE_NAVBAR_ROUTE_ID = {
ACCOUNT: 'account',
SECURITY: 'security',
};

export const ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID = {
MEMBERS: 'members',
SETTINGS: 'settings',
};
38 changes: 20 additions & 18 deletions packages/clerk-js/src/ui/utils/createCustomPages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { isDevelopmentEnvironment } from '@clerk/shared';
import type { CustomPage } from '@clerk/types';

import { isValidUrl } from '../../utils';
import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID, USER_PROFILE_NAVBAR_ROUTE_ID } from '../constants';
import type { NavbarRoute } from '../elements';
import { CogFilled, TickShield, User } from '../icons';
import { localizationKeys } from '../localization';
Expand All @@ -14,7 +15,7 @@ export type CustomPageContent = {
};

type ProfileReorderItem = {
label: 'account' | 'security';
label: 'account' | 'security' | 'members' | 'settings';
};

type ProfileCustomPage = {
Expand Down Expand Up @@ -184,6 +185,7 @@ const checkForDuplicateUsageOfReorderingItems = (customPages: CustomPage[], vali
return [...acc, cp.label];
}, [] as string[]);
};

//path !== '/' && path !== 'account'
const warnForDuplicatePaths = (routes: NavbarRoute[], pathsToFilter: string[]) => {
const paths = routes
Expand Down Expand Up @@ -243,27 +245,27 @@ const getUserProfileDefaultRoutes = (): GetDefaultRoutesReturnType => {
const INITIAL_ROUTES: NavbarRoute[] = [
{
name: localizationKeys('userProfile.start.headerTitle__account'),
id: 'account',
id: USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT,
icon: User,
path: 'account',
},
{
name: localizationKeys('userProfile.start.headerTitle__security'),
id: 'security',
id: USER_PROFILE_NAVBAR_ROUTE_ID.SECURITY,
icon: TickShield,
path: 'account',
},
];

const pageToRootNavbarRouteMap: Record<string, NavbarRoute> = {
profile: INITIAL_ROUTES.find(r => r.id === 'account') as NavbarRoute,
'email-address': INITIAL_ROUTES.find(r => r.id === 'account') as NavbarRoute,
'phone-number': INITIAL_ROUTES.find(r => r.id === 'account') as NavbarRoute,
'connected-account': INITIAL_ROUTES.find(r => r.id === 'account') as NavbarRoute,
'web3-wallet': INITIAL_ROUTES.find(r => r.id === 'account') as NavbarRoute,
username: INITIAL_ROUTES.find(r => r.id === 'account') as NavbarRoute,
'multi-factor': INITIAL_ROUTES.find(r => r.id === 'security') as NavbarRoute,
password: INITIAL_ROUTES.find(r => r.id === 'security') as NavbarRoute,
profile: INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute,
'email-address': INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute,
'phone-number': INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute,
'connected-account': INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute,
'web3-wallet': INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute,
username: INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute,
'multi-factor': INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.SECURITY) as NavbarRoute,
password: INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.SECURITY) as NavbarRoute,
};

const validReorderItemLabels: string[] = INITIAL_ROUTES.map(r => r.id);
Expand All @@ -275,24 +277,24 @@ const getOrganizationProfileDefaultRoutes = (): GetDefaultRoutesReturnType => {
const INITIAL_ROUTES: NavbarRoute[] = [
{
name: localizationKeys('organizationProfile.start.headerTitle__members'),
id: 'members',
id: ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.MEMBERS,
icon: User,
path: 'organization-members',
},
{
name: localizationKeys('organizationProfile.start.headerTitle__settings'),
id: 'settings',
id: ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SETTINGS,
icon: CogFilled,
path: 'organization-settings',
},
];

const pageToRootNavbarRouteMap: Record<string, NavbarRoute> = {
'invite-members': INITIAL_ROUTES.find(r => r.id === 'members') as NavbarRoute,
domain: INITIAL_ROUTES.find(r => r.id === 'settings') as NavbarRoute,
profile: INITIAL_ROUTES.find(r => r.id === 'settings') as NavbarRoute,
leave: INITIAL_ROUTES.find(r => r.id === 'settings') as NavbarRoute,
delete: INITIAL_ROUTES.find(r => r.id === 'settings') as NavbarRoute,
'invite-members': INITIAL_ROUTES.find(r => r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.MEMBERS) as NavbarRoute,
domain: INITIAL_ROUTES.find(r => r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SETTINGS) as NavbarRoute,
profile: INITIAL_ROUTES.find(r => r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SETTINGS) as NavbarRoute,
leave: INITIAL_ROUTES.find(r => r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SETTINGS) as NavbarRoute,
delete: INITIAL_ROUTES.find(r => r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.SETTINGS) as NavbarRoute,
};

const validReorderItemLabels: string[] = INITIAL_ROUTES.map(r => r.id);
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/components/uiComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { logErrorInDevMode } from '@clerk/shared';
import type {
CreateOrganizationProps,
OrganizationListProps,
Expand Down Expand Up @@ -25,7 +26,7 @@ import type {
UserProfilePageProps,
WithClerkProp,
} from '../types';
import { logErrorInDevMode, useOrganizationProfileCustomPages, useUserProfileCustomPages } from '../utils';
import { useOrganizationProfileCustomPages, useUserProfileCustomPages } from '../utils';
import { withClerk } from './withClerk';

type UserProfileExportType = typeof _UserProfile & {
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ export { loadClerkJsScript } from './loadClerkJsScript';
export * from './useMaxAllowedInstancesGuard';
export * from './useCustomElementPortal';
export * from './useCustomPages';
export * from './logErrorInDevMode';
2 changes: 1 addition & 1 deletion packages/react/src/utils/useCustomPages.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { logErrorInDevMode } from '@clerk/shared';
import type { CustomPage } from '@clerk/types';
import type { ReactElement } from 'react';
import React from 'react';
Expand All @@ -10,7 +11,6 @@ import {
} from '../components/uiComponents';
import { customLinkWrongProps, customPagesIgnoredComponent, customPageWrongProps } from '../errors';
import type { UserProfilePageProps } from '../types';
import { logErrorInDevMode } from './logErrorInDevMode';
import type { UseCustomElementPortalParams, UseCustomElementPortalReturn } from './useCustomElementPortal';
import { useCustomElementPortal } from './useCustomElementPortal';

Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { isStaging } from './instance';
export { noop } from './noop';
export * from './runtimeEnvironment';
export * from './runWithExponentialBackOff';
export { logErrorInDevMode } from './logErrorInDevMode';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isDevelopmentEnvironment } from '@clerk/shared';
import { isDevelopmentEnvironment } from './runtimeEnvironment';

export const logErrorInDevMode = (message: string) => {
if (isDevelopmentEnvironment()) {
Expand Down

0 comments on commit 4331688

Please sign in to comment.