Skip to content

Commit

Permalink
feat(clerk-js): Retheme <CreateOrganization/> (#2379)
Browse files Browse the repository at this point in the history
* fix(clerk-js): Fix application logo in OrganizationList

* feat(clerk-js): Retheme CreateOrganization component
  • Loading branch information
anagstef authored Dec 15, 2023
1 parent c5e16d1 commit 3b9e801
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 109 deletions.
2 changes: 2 additions & 0 deletions .changeset/honest-pigs-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { CreateOrganizationModalProps } from '@clerk/types';
import { withOrganizationsEnabledGuard } from '../../common';
import { ComponentContext, withCoreUserGuard } from '../../contexts';
import { Flow } from '../../customizables';
import { ProfileCard, withCardStateProvider } from '../../elements';
import { withCardStateProvider } from '../../elements';
import { Route, Switch } from '../../router';
import type { CreateOrganizationCtx } from '../../types';
import { CreateOrganizationPage } from './CreateOrganizationPage';
Expand All @@ -23,13 +23,7 @@ const _CreateOrganization = () => {
};

const AuthenticatedRoutes = withCoreUserGuard(() => {
return (
<ProfileCard.Root sx={t => ({ width: t.sizes.$120 })}>
<ProfileCard.Content>
<CreateOrganizationPage />
</ProfileCard.Content>
</ProfileCard.Root>
);
return <CreateOrganizationPage />;
});

export const CreateOrganization = withOrganizationsEnabledGuard(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type { OrganizationResource } from '@clerk/types';
import React from 'react';

import { useWizard, Wizard } from '../../common';
import { Icon } from '../../customizables';
import { Col, Icon, Text } from '../../customizables';
import { Form, FormButtonContainer, FormContent, IconButton, SuccessPage, useCardState } from '../../elements';
import { QuestionMark, Upload } from '../../icons';
import { Upload } from '../../icons';
import type { LocalizationKey } from '../../localization';
import { localizationKeys } from '../../localization';
import { colors, createSlug, handleError, useFormControl } from '../../utils';
import { createSlug, handleError, useFormControl } from '../../utils';
import { InviteMembersForm } from '../OrganizationProfile/InviteMembersForm';
import { InvitationsSentMessage } from '../OrganizationProfile/InviteMembersPage';
import { OrganizationProfileAvatarUploader } from '../OrganizationProfile/OrganizationProfileAvatarUploader';
Expand All @@ -19,8 +19,8 @@ type CreateOrganizationFormProps = {
onCancel?: () => void;
onComplete?: () => void;
flow: 'default' | 'organizationList';
startPage: {
headerTitle: LocalizationKey;
startPage?: {
headerTitle?: LocalizationKey;
headerSubtitle?: LocalizationKey;
};
};
Expand Down Expand Up @@ -111,45 +111,59 @@ export const CreateOrganizationForm = (props: CreateOrganizationFormProps) => {
<Wizard {...wizard.props}>
<FormContent
Breadcrumbs={null}
headerTitle={props.startPage.headerTitle}
headerSubtitle={props.startPage.headerSubtitle}
headerTitle={props?.startPage?.headerTitle}
headerSubtitle={props?.startPage?.headerSubtitle}
headerTitleTextVariant={headerTitleTextVariant}
headerSubtitleTextVariant={headerSubtitleTextVariant}
sx={t => ({ minHeight: t.sizes.$60 })}
sx={t => ({ minHeight: t.sizes.$60, gap: 0 })}
>
<Form.Root onSubmit={onSubmit}>
<OrganizationProfileAvatarUploader
organization={{ name: nameField.value }}
onAvatarChange={async file => await setFile(file)}
onAvatarRemove={file ? onAvatarRemove : null}
avatarPreviewPlaceholder={
<IconButton
variant='ghost'
aria-label='Upload organization logo'
icon={
<Icon
size='md'
icon={Upload}
sx={theme => ({
transitionDuration: theme.transitionDuration.$controls,
})}
/>
}
sx={theme => ({
width: theme.sizes.$12,
height: theme.sizes.$12,
borderRadius: theme.radii.$md,
backgroundColor: theme.colors.$avatarBackground,
':hover': {
backgroundColor: colors.makeTransparent(theme.colors.$avatarBackground, 0.2),
svg: {
transform: 'scale(1.2)',
<Col>
<Text
variant='subtitle'
sx={t => ({
textAlign: 'left',
marginBottom: t.space.$2,
color: t.colors.$blackAlpha700,
})}
>
Logo
</Text>
<OrganizationProfileAvatarUploader
organization={{ name: nameField.value }}
onAvatarChange={async file => await setFile(file)}
onAvatarRemove={file ? onAvatarRemove : null}
avatarPreviewPlaceholder={
<IconButton
variant='ghost'
aria-label='Upload organization logo'
icon={
<Icon
size='md'
icon={Upload}
sx={t => ({
color: t.colors.$blackAlpha400,
transitionDuration: t.transitionDuration.$controls,
})}
/>
}
sx={t => ({
width: t.sizes.$16,
height: t.sizes.$16,
borderRadius: t.radii.$md,
border: `${t.borders.$dashed} ${t.colors.$blackAlpha200}`,
backgroundColor: t.colors.$blackAlpha50,
':hover': {
backgroundColor: t.colors.$blackAlpha50,
svg: {
transform: 'scale(1.2)',
},
},
},
})}
/>
}
/>
})}
/>
}
/>
</Col>
<Form.ControlRow elementId={nameField.id}>
<Form.PlainInput
{...nameField.props}
Expand All @@ -163,13 +177,13 @@ export const CreateOrganizationForm = (props: CreateOrganizationFormProps) => {
<Form.PlainInput
{...slugField.props}
sx={{ flexBasis: '80%' }}
icon={QuestionMark}
onChange={onChangeSlug}
isRequired
/>
</Form.ControlRow>
<FormButtonContainer>
<Form.SubmitButton
hasArrow={false}
block={false}
isDisabled={!canSubmit}
localizationKey={localizationKeys('createOrganization.formButtonSubmit')}
Expand All @@ -184,6 +198,7 @@ export const CreateOrganizationForm = (props: CreateOrganizationFormProps) => {
</FormButtonContainer>
</Form.Root>
</FormContent>

<FormContent
Breadcrumbs={null}
headerTitle={localizationKeys('organizationProfile.invitePage.title')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useClerk } from '@clerk/shared/react';

import { useCreateOrganizationContext } from '../../contexts';
import { localizationKeys } from '../../customizables';
import { withCardStateProvider } from '../../elements';
import { Card, Header, withCardStateProvider } from '../../elements';
import { CreateOrganizationForm } from './CreateOrganizationForm';

export const CreateOrganizationPage = withCardStateProvider(() => {
Expand All @@ -12,18 +12,27 @@ export const CreateOrganizationPage = withCardStateProvider(() => {
const { mode, navigateAfterCreateOrganization, skipInvitationScreen } = useCreateOrganizationContext();

return (
<CreateOrganizationForm
skipInvitationScreen={skipInvitationScreen}
navigateAfterCreateOrganization={navigateAfterCreateOrganization}
flow={'default'}
startPage={{
headerTitle: title,
}}
onComplete={() => {
if (mode === 'modal') {
closeCreateOrganization();
}
}}
/>
<Card.Root sx={t => ({ width: t.sizes.$120 })}>
<Header.Root
sx={t => ({
padding: `${t.space.$4} ${t.space.$5}`,
})}
>
<Header.Title localizationKey={title} />
</Header.Root>
<Card.Content sx={t => ({ padding: `${t.space.$5}` })}>
<CreateOrganizationForm
skipInvitationScreen={skipInvitationScreen}
navigateAfterCreateOrganization={navigateAfterCreateOrganization}
flow={'default'}
onComplete={() => {
if (mode === 'modal') {
closeCreateOrganization();
}
}}
/>
</Card.Content>
<Card.Footer />
</Card.Root>
);
});
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { useOrganization, useOrganizationList, useUser } from '@clerk/shared/react';
import { useOrganizationList } from '@clerk/shared/react';
import { useState } from 'react';

import { useEnvironment, useOrganizationListContext } from '../../contexts';
import { Box, Col, descriptors, Flex, localizationKeys, Spinner } from '../../customizables';
import {
Action,
Card,
Header,
OrganizationAvatar,
SecondaryActions,
useCardState,
UserAvatar,
withCardStateProvider,
} from '../../elements';
import { Action, Card, Header, SecondaryActions, useCardState, withCardStateProvider } from '../../elements';
import { useInView } from '../../hooks';
import { Add } from '../../icons';
import { CreateOrganizationForm } from '../CreateOrganization/CreateOrganizationForm';
Expand Down Expand Up @@ -87,7 +78,6 @@ export const OrganizationListPage = withCardStateProvider(() => {
});

const OrganizationListFlows = ({ showListInitially }: { showListInitially: boolean }) => {
const environment = useEnvironment();
const { navigateAfterSelectOrganization, skipInvitationScreen } = useOrganizationListContext();
const [isCreateOrganizationFlow, setCreateOrganizationFlow] = useState(!showListInitially);
return (
Expand All @@ -99,18 +89,19 @@ const OrganizationListFlows = ({ showListInitially }: { showListInitially: boole
{isCreateOrganizationFlow && (
<Box
sx={t => ({
padding: `${t.space.$8}`,
padding: `${t.space.$none} ${t.space.$5} ${t.space.$5}`,
})}
>
<Header.Root
sx={t => ({
padding: `${t.space.$4} ${t.space.$5}`,
})}
>
<Header.Title localizationKey={localizationKeys('createOrganization.title')} />
</Header.Root>
<CreateOrganizationForm
flow='organizationList'
skipInvitationScreen={skipInvitationScreen}
startPage={{
headerTitle: localizationKeys('createOrganization.title'),
headerSubtitle: localizationKeys('organizationList.subtitle', {
applicationName: environment.displayConfig.applicationName,
}),
}}
navigateAfterCreateOrganization={org =>
navigateAfterSelectOrganization(org).then(() => setCreateOrganizationFlow(false))
}
Expand All @@ -129,8 +120,6 @@ const OrganizationListPageList = (props: { onCreateOrganizationClick: () => void

const { ref, userMemberships, userSuggestions, userInvitations } = useOrganizationListInView();
const { hidePersonal } = useOrganizationListContext();
const { organization } = useOrganization();
const { user } = useUser();

const isLoading = userMemberships?.isLoading || userInvitations?.isLoading || userSuggestions?.isLoading;
const hasNextPage = userMemberships?.hasNextPage || userInvitations?.hasNextPage || userSuggestions?.hasNextPage;
Expand All @@ -145,24 +134,6 @@ const OrganizationListPageList = (props: { onCreateOrganizationClick: () => void
padding: `${t.space.$none} ${t.space.$8}`,
})}
>
<Flex
justify='center'
sx={t => ({ paddingBottom: t.space.$6 })}
>
{organization && (
<OrganizationAvatar
{...organization}
size={t => t.sizes.$8}
/>
)}
{!organization && (
<UserAvatar
{...user}
rounded={false}
size={t => t.sizes.$8}
/>
)}
</Flex>
<Header.Title
localizationKey={localizationKeys(
!hidePersonal ? 'organizationList.title' : 'organizationList.titleWithoutPersonal',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ describe('OrganizationList', () => {
await waitFor(() => {
// Header
expect(queryByRole('heading', { name: /Create organization/i })).toBeInTheDocument();
// Subheader
expect(queryByText('to continue to TestApp')).toBeInTheDocument();
//

expect(queryByText('Personal account')).not.toBeInTheDocument();

// Form fields of CreateOrganizationForm
Expand Down
12 changes: 7 additions & 5 deletions packages/clerk-js/src/ui/elements/FormContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { PropsOfComponent } from '../styledSystem';
import { Card, Header, useCardState } from './index';

type PageProps = PropsOfComponent<typeof Col> & {
headerTitle: LocalizationKey | string;
headerTitle?: LocalizationKey | string;
headerTitleTextVariant?: PropsOfComponent<typeof Header.Title>['textVariant'];
breadcrumbTitle?: LocalizationKey;
Breadcrumbs?: React.ComponentType<any> | null;
Expand Down Expand Up @@ -37,10 +37,12 @@ export const FormContent = (props: PageProps) => {
>
<Card.Alert>{card.error}</Card.Alert>
<Header.Root>
<Header.Title
localizationKey={headerTitle}
textVariant='h3'
/>
{headerTitle && (
<Header.Title
localizationKey={headerTitle}
textVariant='h3'
/>
)}
{headerSubtitle && (
<Header.Subtitle
localizationKey={headerSubtitle}
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/src/ui/foundations/borders.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const borders = Object.freeze({
dashed: '1px dashed',
normal: '1px solid',
heavy: '2px solid',
} as const);

0 comments on commit 3b9e801

Please sign in to comment.