diff --git a/.changeset/gold-lamps-appear.md b/.changeset/gold-lamps-appear.md new file mode 100644 index 00000000000..bdacd44a2da --- /dev/null +++ b/.changeset/gold-lamps-appear.md @@ -0,0 +1,6 @@ +--- +"@clerk/clerk-js": minor +"@clerk/types": minor +--- + +Hide sign up url from `` component when mode is `restricted` \ No newline at end of file diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index bbb0e027c5c..f0f2208f716 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -1,6 +1,6 @@ { "files": [ - { "path": "./dist/clerk.browser.js", "maxSize": "64kB" }, + { "path": "./dist/clerk.browser.js", "maxSize": "64.1kB" }, { "path": "./dist/clerk.headless.js", "maxSize": "43kB" }, { "path": "./dist/ui-common*.js", "maxSize": "86KB" }, { "path": "./dist/vendors*.js", "maxSize": "70KB" }, diff --git a/packages/clerk-js/src/core/constants.ts b/packages/clerk-js/src/core/constants.ts index 82a8c3f3207..a025cfc8ce9 100644 --- a/packages/clerk-js/src/core/constants.ts +++ b/packages/clerk-js/src/core/constants.ts @@ -1,3 +1,5 @@ +import type { SignUpModes } from '@clerk/types'; + // TODO: Do we still have a use for this or can we simply preserve all params? export const PRESERVED_QUERYSTRING_PARAMS = [ 'redirect_url', @@ -29,3 +31,8 @@ export const SIGN_IN_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'use export const SIGN_UP_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'username', 'first_name', 'last_name']; export const DEBOUNCE_MS = 350; + +export const SIGN_UP_MODES: Record = { + PUBLIC: 'public', + RESTRICTED: 'restricted', +}; diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index d2b6f838356..4bfb6c6dd57 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -3,7 +3,7 @@ import { isWebAuthnAutofillSupported, isWebAuthnSupported } from '@clerk/shared/ import type { ClerkAPIError, SignInCreateParams, SignInResource } from '@clerk/types'; import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { ERROR_CODES } from '../../../core/constants'; +import { ERROR_CODES, SIGN_UP_MODES } from '../../../core/constants'; import { clerkInvalidFAPIResponse } from '../../../core/errors'; import { getClerkQueryParam, removeClerkQueryParam } from '../../../utils'; import type { SignInStartIdentifier } from '../../common'; @@ -410,13 +410,15 @@ export function _SignInStart(): JSX.Element { - - - - + {userSettings.signUp.mode === SIGN_UP_MODES.PUBLIC && ( + + + + + )} diff --git a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx index 8ee9c1e1cea..b88ad62ae38 100644 --- a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx @@ -111,6 +111,29 @@ describe('SignInStart', () => { }); }); + describe('Restricted mode', () => { + it('"Don\'t have an account?" text should not be presented', async () => { + const { wrapper } = await createFixtures(f => { + f.withEmailAddress(); + f.withRestrictedMode(); + }); + render(, { wrapper }); + expect(screen.queryByText(/Don’t have an account/i)).not.toBeInTheDocument(); + }); + + it('"Don\'t have an account?" text should be visible', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withEmailAddress(); + }); + render(, { wrapper }); + + const signUpLink = screen.getByText(/Don’t have an account/i).nextElementSibling; + expect(signUpLink?.textContent).toBe('Sign up'); + expect(signUpLink?.tagName.toUpperCase()).toBe('A'); + expect(signUpLink?.getAttribute('href')).toMatch(fixtures.environment.displayConfig.signUpUrl); + }); + }); + describe('Social OAuth', () => { it.each(OAUTH_PROVIDERS)('shows the "Continue with $name" social OAuth button', async ({ provider, name }) => { const { wrapper } = await createFixtures(f => { diff --git a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts index 54bf863a191..f429976a28c 100644 --- a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts +++ b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts @@ -17,6 +17,7 @@ import type { VerificationJSON, } from '@clerk/types'; +import { SIGN_UP_MODES } from '../../../core/constants'; import type { OrgParams } from '../../../core/test/fixtures'; import { createUser, getOrganizationId } from '../../../core/test/fixtures'; import { createUserFixture } from './fixtures'; @@ -317,6 +318,8 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => { show_zxcvbn: false, min_zxcvbn_strength: 0, }; + us.sign_up.mode = SIGN_UP_MODES.PUBLIC; + const emptyAttribute = { first_factors: [], second_factors: [], @@ -476,6 +479,10 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => { }; }; + const withRestrictedMode = () => { + us.sign_up.mode = SIGN_UP_MODES.RESTRICTED; + }; + // TODO: Add the rest, consult pkg/generate/auth_config.go return { @@ -493,5 +500,6 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => { withAuthenticatorApp, withPasskey, withPasskeySettings, + withRestrictedMode, }; }; diff --git a/packages/types/src/userSettings.ts b/packages/types/src/userSettings.ts index ca61c419537..39770917446 100644 --- a/packages/types/src/userSettings.ts +++ b/packages/types/src/userSettings.ts @@ -47,10 +47,14 @@ export type SignInData = { }; }; +export type SignUpModes = 'public' | 'restricted'; + export type SignUpData = { allowlist_only: boolean; progressive: boolean; captcha_enabled: boolean; + invite_only_enabled: boolean; + mode: SignUpModes; }; export type PasswordSettingsData = { diff --git a/packages/ui/src/components/sign-in/steps/start.tsx b/packages/ui/src/components/sign-in/steps/start.tsx index 6c0d18ebd5d..9c162242db1 100644 --- a/packages/ui/src/components/sign-in/steps/start.tsx +++ b/packages/ui/src/components/sign-in/steps/start.tsx @@ -11,12 +11,14 @@ import { PhoneNumberField } from '~/common/phone-number-field'; import { PhoneNumberOrUsernameField } from '~/common/phone-number-or-username-field'; import { UsernameField } from '~/common/username-field'; import { LOCALIZATION_NEEDED } from '~/constants/localizations'; +import { SIGN_UP_MODES } from '~/constants/user-settings'; import { useAppearance } from '~/contexts'; import { useAttributes } from '~/hooks/use-attributes'; import { useCard } from '~/hooks/use-card'; import { useDevModeWarning } from '~/hooks/use-dev-mode-warning'; import { useDisplayConfig } from '~/hooks/use-display-config'; import { useEnabledConnections } from '~/hooks/use-enabled-connections'; +import { useEnvironment } from '~/hooks/use-environment'; import { useLocalizations } from '~/hooks/use-localizations'; import { Button } from '~/primitives/button'; import * as Card from '~/primitives/card'; @@ -27,6 +29,7 @@ import { Separator } from '~/primitives/separator'; export function SignInStart() { const enabledConnections = useEnabledConnections(); const { t } = useLocalizations(); + const { userSettings } = useEnvironment(); const { enabled: usernameEnabled } = useAttributes('username'); const { enabled: phoneNumberEnabled } = useAttributes('phone_number'); const { enabled: emailAddressEnabled } = useAttributes('email_address'); @@ -184,12 +187,14 @@ export function SignInStart() { - - - {t('signIn.start.actionText')}{' '} - {t('signIn.start.actionLink')} - - + {userSettings.signUp.mode === SIGN_UP_MODES.PUBLIC ? ( + + + {t('signIn.start.actionText')}{' '} + {t('signIn.start.actionLink')} + + + ) : null} diff --git a/packages/ui/src/constants/user-settings.ts b/packages/ui/src/constants/user-settings.ts new file mode 100644 index 00000000000..54db661f8b1 --- /dev/null +++ b/packages/ui/src/constants/user-settings.ts @@ -0,0 +1,6 @@ +import type { SignUpModes } from '@clerk/types'; + +export const SIGN_UP_MODES: Record = { + PUBLIC: 'public', + RESTRICTED: 'restricted', +};