From 9b2aeacb32fff7c300bda458636a1cc81a42ee7b Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Mon, 5 Aug 2024 13:21:39 -0700 Subject: [PATCH] feat(clerk-js,types): Add hideSlug prop to Organization components (#3882) Co-authored-by: panteliselef --- .changeset/polite-tigers-smoke.md | 6 ++++ .../CreateOrganizationForm.tsx | 31 +++++++++++------- .../CreateOrganizationPage.tsx | 3 +- .../__tests__/CreateOrganization.test.tsx | 32 +++++++++++++++++++ .../OrganizationList/OrganizationListPage.tsx | 3 +- .../__tests__/OrganizationList.test.tsx | 20 ++++++++++++ .../OrganizationSwitcherPopover.tsx | 3 +- .../__tests__/OrganizationSwitcher.test.tsx | 17 ++++++++++ .../ui/contexts/ClerkUIComponentsContext.tsx | 3 ++ packages/types/src/clerk.ts | 15 +++++++++ 10 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 .changeset/polite-tigers-smoke.md diff --git a/.changeset/polite-tigers-smoke.md b/.changeset/polite-tigers-smoke.md new file mode 100644 index 00000000000..58a9f74eacc --- /dev/null +++ b/.changeset/polite-tigers-smoke.md @@ -0,0 +1,6 @@ +--- +"@clerk/clerk-js": patch +"@clerk/types": patch +--- + +Add option to hide the slug field in the ``, ``, and `` components diff --git a/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx b/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx index f76a183cb0b..df3cf591482 100644 --- a/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx +++ b/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx @@ -1,5 +1,5 @@ import { useOrganization, useOrganizationList } from '@clerk/shared/react'; -import type { OrganizationResource } from '@clerk/types'; +import type { CreateOrganizationParams, OrganizationResource } from '@clerk/types'; import React from 'react'; import { useWizard, Wizard } from '../../common'; @@ -33,6 +33,7 @@ type CreateOrganizationFormProps = { headerTitle?: LocalizationKey; headerSubtitle?: LocalizationKey; }; + hideSlug?: boolean; }; export const CreateOrganizationForm = withCardStateProvider((props: CreateOrganizationFormProps) => { @@ -72,7 +73,13 @@ export const CreateOrganizationForm = withCardStateProvider((props: CreateOrgani } try { - const organization = await createOrganization({ name: nameField.value, slug: slugField.value }); + const createOrgParams: CreateOrganizationParams = { name: nameField.value }; + + if (!props.hideSlug) { + createOrgParams.slug = slugField.value; + } + + const organization = await createOrganization(createOrgParams); if (file) { await organization.setLogo({ file }); } @@ -181,15 +188,17 @@ export const CreateOrganizationForm = withCardStateProvider((props: CreateOrgani ignorePasswordManager /> - - - + {!props.hideSlug && ( + + + + )} ({ marginTop: t.space.$none })}> { const { closeCreateOrganization } = useClerk(); - const { mode, navigateAfterCreateOrganization, skipInvitationScreen } = useCreateOrganizationContext(); + const { mode, navigateAfterCreateOrganization, skipInvitationScreen, hideSlug } = useCreateOrganizationContext(); const card = useCardState(); const { showDevModeNotice } = useDevMode(); @@ -31,6 +31,7 @@ export const CreateOrganizationPage = withCardStateProvider(() => { closeCreateOrganization(); } }} + hideSlug={hideSlug} /> diff --git a/packages/clerk-js/src/ui/components/CreateOrganization/__tests__/CreateOrganization.test.tsx b/packages/clerk-js/src/ui/components/CreateOrganization/__tests__/CreateOrganization.test.tsx index 5711f0d89f5..da9b3a1e7af 100644 --- a/packages/clerk-js/src/ui/components/CreateOrganization/__tests__/CreateOrganization.test.tsx +++ b/packages/clerk-js/src/ui/components/CreateOrganization/__tests__/CreateOrganization.test.tsx @@ -79,6 +79,38 @@ describe('CreateOrganization', () => { expect(getByRole('heading', { name: 'Create organization', level: 1 })).toBeInTheDocument(); }); + it('renders component without slug field', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + }); + }); + + fixtures.clerk.createOrganization.mockReturnValue( + Promise.resolve( + getCreatedOrg({ + maxAllowedMemberships: 1, + slug: 'new-org-1722578361', + }), + ), + ); + + props.setProps({ hideSlug: true }); + const { userEvent, getByRole, queryByText, queryByLabelText, getByLabelText } = render(, { + wrapper, + }); + + expect(queryByLabelText(/Slug/i)).not.toBeInTheDocument(); + + await userEvent.type(getByLabelText(/Name/i), 'new org'); + await userEvent.click(getByRole('button', { name: /create organization/i })); + + await waitFor(() => { + expect(queryByText(/Invite new members/i)).toBeInTheDocument(); + }); + }); + it('skips invitation screen', async () => { const { wrapper, fixtures, props } = await createFixtures(f => { f.withOrganizations(); diff --git a/packages/clerk-js/src/ui/components/OrganizationList/OrganizationListPage.tsx b/packages/clerk-js/src/ui/components/OrganizationList/OrganizationListPage.tsx index f3e994e3497..8e6ec776c26 100644 --- a/packages/clerk-js/src/ui/components/OrganizationList/OrganizationListPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationList/OrganizationListPage.tsx @@ -109,7 +109,7 @@ export const OrganizationListPage = withCardStateProvider(() => { }); const OrganizationListFlows = ({ showListInitially }: { showListInitially: boolean }) => { - const { navigateAfterCreateOrganization, skipInvitationScreen } = useOrganizationListContext(); + const { navigateAfterCreateOrganization, skipInvitationScreen, hideSlug } = useOrganizationListContext(); const [isCreateOrganizationFlow, setCreateOrganizationFlow] = useState(!showListInitially); return ( <> @@ -133,6 +133,7 @@ const OrganizationListFlows = ({ showListInitially }: { showListInitially: boole onCancel={ showListInitially && isCreateOrganizationFlow ? () => setCreateOrganizationFlow(false) : undefined } + hideSlug={hideSlug} /> )} 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 9b5e299a25b..fb58320fce9 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 @@ -256,6 +256,26 @@ describe('OrganizationList', () => { }); }); + it('displays CreateOrganization without slug field', async () => { + const { wrapper, props } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + create_organization_enabled: true, + }); + }); + + props.setProps({ hideSlug: true }); + const { findByRole, getByRole, userEvent, queryByLabelText } = render(, { wrapper }); + + await waitFor(async () => + expect(await findByRole('menuitem', { name: 'Create organization' })).toBeInTheDocument(), + ); + await userEvent.click(getByRole('menuitem', { name: 'Create organization' })); + expect(queryByLabelText(/Name/i)).toBeInTheDocument(); + expect(queryByLabelText(/Slug/i)).not.toBeInTheDocument(); + }); + it('does not display CreateOrganization within OrganizationList when disabled', async () => { const { wrapper } = await createFixtures(f => { f.withOrganizations(); diff --git a/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx b/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx index 0a546370ab0..7edecbf80ee 100644 --- a/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationSwitcher/OrganizationSwitcherPopover.tsx @@ -49,6 +49,7 @@ export const OrganizationSwitcherPopover = React.forwardRef { 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 1f5d1a9ebdd..60a9fb997ba 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 @@ -223,6 +223,23 @@ describe('OrganizationSwitcher', () => { expect(fixtures.clerk.openCreateOrganization).toHaveBeenCalled(); }); + it('opens create organization without slug field', async () => { + const { wrapper, fixtures, props } = await createFixtures(f => { + f.withOrganizations(); + f.withUser({ + email_addresses: ['test@clerk.com'], + create_organization_enabled: true, + }); + }); + + props.setProps({ hideSlug: true }); + const { getByRole, queryByLabelText, userEvent } = render(, { wrapper }); + await userEvent.click(getByRole('button', { name: 'Open organization switcher' })); + await userEvent.click(getByRole('menuitem', { name: 'Create organization' })); + expect(fixtures.clerk.openCreateOrganization).toHaveBeenCalled(); + expect(queryByLabelText(/Slug/i)).not.toBeInTheDocument(); + }); + it('does not display create organization button if permissions not present', async () => { const { wrapper, props } = await createFixtures(f => { f.withOrganizations(); diff --git a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx index 60cc4659156..118ecb224db 100644 --- a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx +++ b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx @@ -361,6 +361,7 @@ export const useOrganizationSwitcherContext = () => { organizationProfileMode: organizationProfileMode || 'modal', createOrganizationMode: createOrganizationMode || 'modal', skipInvitationScreen: ctx.skipInvitationScreen || false, + hideSlug: ctx.hideSlug || false, afterCreateOrganizationUrl, afterLeaveOrganizationUrl, navigateOrganizationProfile, @@ -440,6 +441,7 @@ export const useOrganizationListContext = () => { ...ctx, afterCreateOrganizationUrl, skipInvitationScreen: ctx.skipInvitationScreen || false, + hideSlug: ctx.hideSlug || false, hidePersonal: ctx.hidePersonal || false, navigateAfterCreateOrganization, navigateAfterSelectOrganization, @@ -515,6 +517,7 @@ export const useCreateOrganizationContext = () => { return { ...ctx, skipInvitationScreen: ctx.skipInvitationScreen || false, + hideSlug: ctx.hideSlug || false, navigateAfterCreateOrganization, componentName, }; diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 274a71311d8..c17dafa45f9 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -866,6 +866,11 @@ export type CreateOrganizationProps = RoutingOptions & { * prop of ClerkProvided (if one is provided) */ appearance?: CreateOrganizationTheme; + /** + * Hides the optional "slug" field in the organization creation screen. + * @default false + */ + hideSlug?: boolean; }; export type CreateOrganizationModalProps = WithoutRouting; @@ -996,6 +1001,11 @@ export type OrganizationSwitcherProps = CreateOrganizationMode & * the number of max allowed members is equal to 1 */ skipInvitationScreen?: boolean; + /** + * Hides the optional "slug" field in the organization creation screen. + * @default false + */ + hideSlug?: boolean; /** * Customisation options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` @@ -1051,6 +1061,11 @@ export type OrganizationListProps = { * @default undefined` */ afterSelectPersonalUrl?: ((user: UserResource) => string) | LooseExtractedParams>; + /** + * Hides the optional "slug" field in the organization creation screen. + * @default false + */ + hideSlug?: boolean; }; export interface HandleEmailLinkVerificationParams {