From fface15a2c853f740bde87710233d422f1e1ebb2 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 23 Oct 2024 23:18:57 -0400 Subject: [PATCH 01/11] wip --- .../machines/sign-up/continue.machine.ts | 18 ++++- .../sign-up/utils/fields-to-params.ts | 16 ++++- .../third-party/third-party.machine.ts | 9 ++- .../elements/src/react/common/connections.tsx | 1 + packages/ui/src/common/legal-accepted.tsx | 68 +++++++++++++++++++ packages/ui/src/common/link-renderer.tsx | 44 ++++++++++++ .../src/components/sign-up/steps/continue.tsx | 8 +++ .../ui/src/components/sign-up/steps/start.tsx | 7 +- packages/ui/src/utils/make-localizable.ts | 5 ++ 9 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 packages/ui/src/common/legal-accepted.tsx create mode 100644 packages/ui/src/common/link-renderer.tsx diff --git a/packages/elements/src/internals/machines/sign-up/continue.machine.ts b/packages/elements/src/internals/machines/sign-up/continue.machine.ts index 561d57604c..40cdc0a600 100644 --- a/packages/elements/src/internals/machines/sign-up/continue.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/continue.machine.ts @@ -1,7 +1,7 @@ import { snakeToCamel } from '@clerk/shared/underscore'; import type { SignUpResource } from '@clerk/types'; import type { DoneActorEvent } from 'xstate'; -import { fromPromise, setup } from 'xstate'; +import { fromPromise, not, or, setup } from 'xstate'; import { SIGN_UP_DEFAULT_BASE_PATH } from '~/internals/constants'; import type { FormDefaultValues, FormFields } from '~/internals/machines/form'; @@ -62,6 +62,21 @@ export const SignUpContinueMachine = setup({ context.parent.send({ type: 'NEXT', resource: (event as unknown as DoneActorEvent).output }), sendToLoading, }, + guards: { + isMissingRequirements: ({ context }) => + context.parent.getSnapshot().context.clerk?.client?.signUp?.status === 'missing_requirements', + requirementsMet: ({ context }) => { + const signUp = context.parent.getSnapshot().context.clerk.client.signUp; + + const fields = context.formRef.getSnapshot().context.fields; + const signUpMissingFields = signUp.missingFields.map(snakeToCamel); + const missingFields = Array.from(context.formRef.getSnapshot().context.fields.keys()).filter(key => { + return !signUpMissingFields.includes(key) && !fields.get(key)?.value && !fields.get(key)?.checked; + }); + + return missingFields.length === 0; + }, + }, types: {} as SignUpContinueSchema, }).createMachine({ id: SignUpContinueMachineId, @@ -82,6 +97,7 @@ export const SignUpContinueMachine = setup({ description: 'Waiting for user input', on: { SUBMIT: { + guard: or(['requirementsMet', not('isMissingRequirements')]), target: 'Attempting', reenter: true, }, diff --git a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts index e704b9d00b..7a2d793251 100644 --- a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts +++ b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts @@ -2,7 +2,15 @@ import type { SignUpCreateParams, SignUpUpdateParams } from '@clerk/types'; import type { FormFields } from '~/internals/machines/form'; -const SignUpAdditionalKeys = ['firstName', 'lastName', 'emailAddress', 'username', 'password', 'phoneNumber'] as const; +const SignUpAdditionalKeys = [ + 'firstName', + 'lastName', + 'emailAddress', + 'username', + 'password', + 'phoneNumber', + '__experimental_legalAccepted', +] as const; type SignUpAdditionalKeys = (typeof SignUpAdditionalKeys)[number]; @@ -17,10 +25,14 @@ export function fieldsToSignUpParams { const params: SignUpUpdateParams = {}; - fields.forEach(({ value }, key) => { + fields.forEach(({ value, checked }, key) => { if (isSignUpParam(key) && value !== undefined) { params[key] = value as string; } + + if (isSignUpParam(key) && checked !== undefined) { + params[key] = checked as boolean; + } }); return params; diff --git a/packages/elements/src/internals/machines/third-party/third-party.machine.ts b/packages/elements/src/internals/machines/third-party/third-party.machine.ts index fdca1ba57e..ad2d7d05cd 100644 --- a/packages/elements/src/internals/machines/third-party/third-party.machine.ts +++ b/packages/elements/src/internals/machines/third-party/third-party.machine.ts @@ -77,10 +77,17 @@ export const ThirdPartyMachine = setup({ input: ({ context, event }) => { assertEvent(event, 'REDIRECT'); + const legalAcceptedField = context.formRef + .getSnapshot() + .context.fields.get('__experimental_legalAccepted')?.checked; + return { basePath: context.basePath, flow: context.flow, - params: event.params, + params: { + ...event.params, + __experimental_legalAccepted: legalAcceptedField || undefined, + }, parent: context.parent, }; }, diff --git a/packages/elements/src/react/common/connections.tsx b/packages/elements/src/react/common/connections.tsx index 0f94933774..6c074c06f5 100644 --- a/packages/elements/src/react/common/connections.tsx +++ b/packages/elements/src/react/common/connections.tsx @@ -30,6 +30,7 @@ export const useConnectionContext = () => { export interface ConnectionProps extends React.ButtonHTMLAttributes { asChild?: boolean; name: OAuthProvider | Web3Provider | SamlStrategy; + legalAcceptence?: boolean; } /** diff --git a/packages/ui/src/common/legal-accepted.tsx b/packages/ui/src/common/legal-accepted.tsx new file mode 100644 index 0000000000..1d95650739 --- /dev/null +++ b/packages/ui/src/common/legal-accepted.tsx @@ -0,0 +1,68 @@ +import * as Common from '@clerk/elements/common'; +import React from 'react'; + +import { useAppearance } from '~/contexts'; +import { useEnvironment } from '~/hooks/use-environment'; +import { useLocalizations } from '~/hooks/use-localizations'; +import * as Field from '~/primitives/field'; + +import { LinkRenderer } from './link-renderer'; + +export function LegalAcceptedField(props: Omit, 'type'>) { + const { t } = useLocalizations(); + const { displayConfig } = useEnvironment(); + const { parsedAppearance } = useAppearance(); + const termsUrl = parsedAppearance.options.termsPageUrl || displayConfig.termsUrl; + const privacyPolicyUrl = parsedAppearance.options.privacyPageUrl || displayConfig.privacyPolicyUrl; + + let localizedText: string | undefined; + + if (termsUrl && privacyPolicyUrl) { + localizedText = t('signUp.__experimental_legalConsent.checkbox.label__termsOfServiceAndPrivacyPolicy', { + termsOfServiceLink: termsUrl, + privacyPolicyLink: privacyPolicyUrl, + }); + } else if (termsUrl) { + localizedText = t('signUp.__experimental_legalConsent.checkbox.label__onlyTermsOfService', { + termsOfServiceLink: termsUrl, + }); + } else if (privacyPolicyUrl) { + localizedText = t('signUp.__experimental_legalConsent.checkbox.label__onlyPrivacyPolicy', { + privacyPolicyLink: privacyPolicyUrl, + }); + } + + return ( + + +
+ + + + + + + + + +
+ + + {({ message }) => { + return {message}; + }} + +
+
+ ); +} diff --git a/packages/ui/src/common/link-renderer.tsx b/packages/ui/src/common/link-renderer.tsx new file mode 100644 index 0000000000..98c2e470d2 --- /dev/null +++ b/packages/ui/src/common/link-renderer.tsx @@ -0,0 +1,44 @@ +import React, { memo, useMemo } from 'react'; + +interface LinkRendererProps extends Omit, 'href' | 'children' | 'class'> { + text: string; + className?: string; +} + +const LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g; // parses [text](url) + +export const LinkRenderer: React.FC = memo(({ text, ...linkProps }) => { + const memoizedLinkProps = useMemo(() => linkProps, [linkProps]); + + const renderedContent = useMemo(() => { + const parts: (string | JSX.Element)[] = []; + let lastIndex = 0; + + text.replace(LINK_REGEX, (match, linkText, url, offset) => { + if (offset > lastIndex) { + parts.push(text.slice(lastIndex, offset)); + } + parts.push( + + {linkText} + , + ); + lastIndex = offset + match.length; + return match; + }); + + if (lastIndex < text.length) { + parts.push(text.slice(lastIndex)); + } + + return parts; + }, [text, memoizedLinkProps]); + + return renderedContent; +}); diff --git a/packages/ui/src/components/sign-up/steps/continue.tsx b/packages/ui/src/components/sign-up/steps/continue.tsx index 3690611737..5583b7127d 100644 --- a/packages/ui/src/components/sign-up/steps/continue.tsx +++ b/packages/ui/src/components/sign-up/steps/continue.tsx @@ -6,6 +6,7 @@ import { EmailField } from '~/common/email-field'; import { FirstNameField } from '~/common/first-name-field'; import { GlobalError } from '~/common/global-error'; import { LastNameField } from '~/common/last-name-field'; +import { LegalAcceptedField } from '~/common/legal-accepted'; import { PasswordField } from '~/common/password-field'; import { PhoneNumberField } from '~/common/phone-number-field'; import { RouterLink } from '~/common/router-link'; @@ -14,6 +15,7 @@ import { LOCALIZATION_NEEDED } from '~/constants/localizations'; import { useAttributes } from '~/hooks/use-attributes'; import { useCard } from '~/hooks/use-card'; import { useDevModeWarning } from '~/hooks/use-dev-mode-warning'; +import { useEnvironment } from '~/hooks/use-environment'; import { useLocalizations } from '~/hooks/use-localizations'; import { useOptions } from '~/hooks/use-options'; import { Button } from '~/primitives/button'; @@ -24,11 +26,15 @@ export function SignUpContinue() { const clerk = useClerk(); const { signInUrl } = useOptions(); const { t } = useLocalizations(); + const environment = useEnvironment(); + const { client } = useClerk(); const { enabled: firstNameEnabled, required: firstNameRequired } = useAttributes('first_name'); const { enabled: lastNameEnabled, required: lastNameRequired } = useAttributes('last_name'); const { enabled: usernameEnabled, required: usernameRequired } = useAttributes('username'); const { enabled: phoneNumberEnabled, required: phoneNumberRequired } = useAttributes('phone_number'); const { enabled: passwordEnabled, required: passwordRequired } = useAttributes('password'); + const legalConsentEnabled = + environment.userSettings.signUp.legal_consent_enabled && client.signUp.missingFields.includes('legal_accepted'); const isDev = useDevModeWarning(); const { logoProps, footerProps } = useCard(); @@ -92,6 +98,8 @@ export function SignUpContinue() { disabled={isGlobalLoading} /> ) : null} + + {legalConsentEnabled && } diff --git a/packages/ui/src/components/sign-up/steps/start.tsx b/packages/ui/src/components/sign-up/steps/start.tsx index 90450609b6..576ffe7666 100644 --- a/packages/ui/src/components/sign-up/steps/start.tsx +++ b/packages/ui/src/components/sign-up/steps/start.tsx @@ -8,6 +8,7 @@ import { EmailOrPhoneNumberField } from '~/common/email-or-phone-number-field'; import { FirstNameField } from '~/common/first-name-field'; import { GlobalError } from '~/common/global-error'; import { LastNameField } from '~/common/last-name-field'; +import { LegalAcceptedField } from '~/common/legal-accepted'; import { PasswordField } from '~/common/password-field'; import { PhoneNumberField } from '~/common/phone-number-field'; import { RouterLink } from '~/common/router-link'; @@ -54,6 +55,7 @@ export function SignUpStart() { const isDev = useDevModeWarning(); const { options } = useAppearance().parsedAppearance; const { logoProps, footerProps } = useCard(); + const legalConsentEnabled = userSettings.signUp.legal_consent_enabled; return ( @@ -144,10 +146,13 @@ export function SignUpStart() { {options.socialButtonsPlacement === 'bottom' ? connectionsWithSeperator.reverse() : null} + {!(hasConnection && hasIdentifier && legalConsentEnabled) && } + {userSettings.signUp.captcha_enabled ? : null} - {hasConnection || hasIdentifier ? ( + {hasIdentifier ? ( + {legalConsentEnabled && hasIdentifier && } {isSubmitting => { return ( diff --git a/packages/ui/src/utils/make-localizable.ts b/packages/ui/src/utils/make-localizable.ts index 2e651b8b97..3b8cd0bcb1 100644 --- a/packages/ui/src/utils/make-localizable.ts +++ b/packages/ui/src/utils/make-localizable.ts @@ -161,11 +161,16 @@ const numeric = (val: Date | number | string, locale?: string) => { } }; +const link = (val: string, label?: string) => { + return `[${label}](${val})`; +}; + const MODIFIERS = { titleize, timeString, weekday, numeric, + link, } as const; const applyTokenExpressions = (s: string, expressions: TokenExpression[], tokens: Tokens) => { From 8d8afd3e8f9c15eea3d34a194f032c9ea75db138 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 00:20:51 +0200 Subject: [PATCH 02/11] wip --- packages/ui/src/common/legal-accepted.tsx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/common/legal-accepted.tsx b/packages/ui/src/common/legal-accepted.tsx index 1d95650739..3036569f79 100644 --- a/packages/ui/src/common/legal-accepted.tsx +++ b/packages/ui/src/common/legal-accepted.tsx @@ -1,4 +1,5 @@ import * as Common from '@clerk/elements/common'; +import { cx } from 'cva'; import React from 'react'; import { useAppearance } from '~/contexts'; @@ -8,7 +9,10 @@ import * as Field from '~/primitives/field'; import { LinkRenderer } from './link-renderer'; -export function LegalAcceptedField(props: Omit, 'type'>) { +export function LegalAcceptedField({ + className, + ...restProps +}: Omit, 'type'>) { const { t } = useLocalizations(); const { displayConfig } = useEnvironment(); const { parsedAppearance } = useAppearance(); @@ -38,21 +42,24 @@ export function LegalAcceptedField(props: Omit -
+
- - + + + +
From 6795ae55540e46dd805a78895211d6605db9c014 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 00:22:01 +0200 Subject: [PATCH 03/11] chore(repo): Add changeset --- .changeset/odd-peas-hear.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/odd-peas-hear.md diff --git a/.changeset/odd-peas-hear.md b/.changeset/odd-peas-hear.md new file mode 100644 index 0000000000..603f381a00 --- /dev/null +++ b/.changeset/odd-peas-hear.md @@ -0,0 +1,5 @@ +--- +"@clerk/elements": minor +--- + +Adding support for legal consent From 144c5a4e8c507019acbfe648ce96a2b6c3656740 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 02:00:15 +0200 Subject: [PATCH 04/11] wip --- .../elements/src/internals/machines/form/form.machine.ts | 9 +++++++-- packages/elements/src/react/common/connections.tsx | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/elements/src/internals/machines/form/form.machine.ts b/packages/elements/src/internals/machines/form/form.machine.ts index 28393b59f1..ccf1190863 100644 --- a/packages/elements/src/internals/machines/form/form.machine.ts +++ b/packages/elements/src/internals/machines/form/form.machine.ts @@ -74,9 +74,14 @@ export const FormMachine = setup({ throw new Error('Field name is required'); } - if (context.fields.has(params.name)) { + const fieldsNameMap: Record = { + legalAccepted: '__experimental_legalAccepted', + }; + const fieldName = fieldsNameMap[params.name] || params.name; + + if (context.fields.has(fieldName)) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - context.fields.get(params.name)!.feedback = params.feedback; + context.fields.get(fieldName)!.feedback = params.feedback; } return context.fields; diff --git a/packages/elements/src/react/common/connections.tsx b/packages/elements/src/react/common/connections.tsx index 6c074c06f5..0f94933774 100644 --- a/packages/elements/src/react/common/connections.tsx +++ b/packages/elements/src/react/common/connections.tsx @@ -30,7 +30,6 @@ export const useConnectionContext = () => { export interface ConnectionProps extends React.ButtonHTMLAttributes { asChild?: boolean; name: OAuthProvider | Web3Provider | SamlStrategy; - legalAcceptence?: boolean; } /** From 3d3e50afaceacdf1868297987eae3e7330bdb194 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 15:42:18 +0200 Subject: [PATCH 05/11] wip --- .../src/ui/components/SignUp/SignUpStart.tsx | 2 +- .../ui/customizables/elementDescriptors.ts | 2 + .../clerk-js/src/ui/elements/FieldControl.tsx | 44 +++++++++++-------- .../src/ui/elements/LegalConsentCheckbox.tsx | 7 ++- .../sign-up/utils/fields-to-params.ts | 6 ++- packages/types/src/appearance.ts | 2 + packages/ui/src/common/legal-accepted.tsx | 2 + .../src/components/sign-up/steps/continue.tsx | 24 ++++++---- 8 files changed, 58 insertions(+), 31 deletions(-) diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx index ff059fccae..3ef614a3bc 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx @@ -281,7 +281,7 @@ function _SignUpStart(): JSX.Element { enableOAuthProviders={showOauthProviders} enableWeb3Providers={showWeb3Providers} continueSignUp={missingRequirementsWithTicket} - legalAccepted={Boolean(formState.__experimental_legalAccepted.checked)} + legalAccepted={Boolean(formState.__experimental_legalAccepted.checked) || undefined} /> )} {shouldShowForm && ( diff --git a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts index 3a2cb8cc19..c60bab033a 100644 --- a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts +++ b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts @@ -91,6 +91,8 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'formFieldRadioLabel', 'formFieldRadioLabelTitle', 'formFieldRadioLabelDescription', + 'formFieldCheckboxInput', + 'formFieldCheckboxLabel', 'formFieldAction', 'formFieldInput', 'formFieldErrorText', diff --git a/packages/clerk-js/src/ui/elements/FieldControl.tsx b/packages/clerk-js/src/ui/elements/FieldControl.tsx index 18ea8f9854..545f339ee2 100644 --- a/packages/clerk-js/src/ui/elements/FieldControl.tsx +++ b/packages/clerk-js/src/ui/elements/FieldControl.tsx @@ -15,6 +15,7 @@ import { Text, useLocalizations, } from '../customizables'; +import type { ElementDescriptor, ElementId } from '../customizables/elementDescriptors'; import { FormFieldContextProvider, sanitizeInputProps, useFormField } from '../primitives/hooks'; import type { PropsOfComponent } from '../styledSystem'; import type { useFormControl as useFormControlUtil } from '../utils'; @@ -208,25 +209,32 @@ const PasswordInputElement = forwardRef((_, ref) => { ); }); -const CheckboxIndicator = forwardRef((_, ref) => { - const formField = useFormField(); - const { placeholder, ...inputProps } = sanitizeInputProps(formField); +type CheckboxIndicatorProps = { + elementDescriptor?: ElementDescriptor; + elementId?: ElementId; +}; - return ( - ({ - width: 'fit-content', - flexShrink: 0, - marginTop: t.space.$0x5, - })} - /> - ); -}); +const CheckboxIndicator = forwardRef( + ({ elementDescriptor, elementId }, ref) => { + const formField = useFormField(); + const { placeholder, ...inputProps } = sanitizeInputProps(formField); + + return ( + ({ + width: 'fit-content', + flexShrink: 0, + marginTop: t.space.$0x5, + })} + /> + ); + }, +); const CheckboxLabel = (props: { description?: string | LocalizationKey }) => { const { label, id } = useFormField(); diff --git a/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx b/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx index 4f2ac72bd0..005688be4c 100644 --- a/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx +++ b/packages/clerk-js/src/ui/elements/LegalConsentCheckbox.tsx @@ -69,9 +69,12 @@ export const LegalCheckbox = ( return ( - + ({ paddingLeft: t.space.$1x5, diff --git a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts index 7a2d793251..d0a33b5bed 100644 --- a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts +++ b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts @@ -25,15 +25,17 @@ export function fieldsToSignUpParams { const params: SignUpUpdateParams = {}; - fields.forEach(({ value, checked }, key) => { + fields.forEach(({ value, checked, type }, key) => { if (isSignUpParam(key) && value !== undefined) { params[key] = value as string; } - if (isSignUpParam(key) && checked !== undefined) { + if (isSignUpParam(key) && type === 'checkbox') { params[key] = checked as boolean; } }); + console.log(params); + return params; } diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index 88b566cb38..cc7173cdaf 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -210,6 +210,8 @@ export type ElementsConfig = { formFieldRadioLabel: WithOptions; formFieldRadioLabelTitle: WithOptions; formFieldRadioLabelDescription: WithOptions; + formFieldCheckboxInput: WithOptions; + formFieldCheckboxLabel: WithOptions; formFieldAction: WithOptions; formFieldInput: WithOptions; formFieldErrorText: WithOptions; diff --git a/packages/ui/src/common/legal-accepted.tsx b/packages/ui/src/common/legal-accepted.tsx index 3036569f79..bc8fd89bf6 100644 --- a/packages/ui/src/common/legal-accepted.tsx +++ b/packages/ui/src/common/legal-accepted.tsx @@ -11,6 +11,7 @@ import { LinkRenderer } from './link-renderer'; export function LegalAcceptedField({ className, + checked = false, ...restProps }: Omit, 'type'>) { const { t } = useLocalizations(); @@ -47,6 +48,7 @@ export function LegalAcceptedField({ type='checkbox' asChild className={cx('flex-shrink-0', className)} + checked={checked} {...restProps} > diff --git a/packages/ui/src/components/sign-up/steps/continue.tsx b/packages/ui/src/components/sign-up/steps/continue.tsx index 5583b7127d..35ef4e519c 100644 --- a/packages/ui/src/components/sign-up/steps/continue.tsx +++ b/packages/ui/src/components/sign-up/steps/continue.tsx @@ -28,13 +28,21 @@ export function SignUpContinue() { const { t } = useLocalizations(); const environment = useEnvironment(); const { client } = useClerk(); + const { missingFields } = client.signUp; const { enabled: firstNameEnabled, required: firstNameRequired } = useAttributes('first_name'); const { enabled: lastNameEnabled, required: lastNameRequired } = useAttributes('last_name'); const { enabled: usernameEnabled, required: usernameRequired } = useAttributes('username'); const { enabled: phoneNumberEnabled, required: phoneNumberRequired } = useAttributes('phone_number'); const { enabled: passwordEnabled, required: passwordRequired } = useAttributes('password'); - const legalConsentEnabled = - environment.userSettings.signUp.legal_consent_enabled && client.signUp.missingFields.includes('legal_accepted'); + const legalConsentEnabled = environment.userSettings.signUp.legal_consent_enabled; + const legalConsentMissing = missingFields.includes('legal_accepted'); + const showFirstName = firstNameEnabled && firstNameRequired && missingFields.includes('first_name'); + const showLastName = lastNameEnabled && lastNameRequired && missingFields.includes('last_name'); + const showUserName = usernameEnabled && usernameRequired && missingFields.includes('username'); + const showPhoneNumber = phoneNumberEnabled && phoneNumberRequired && missingFields.includes('phone_number'); + const showPassword = passwordEnabled && passwordRequired && missingFields.includes('password'); + const showEmail = missingFields.includes('email_address'); + const showLegalConsent = legalConsentEnabled && legalConsentMissing; const isDev = useDevModeWarning(); const { logoProps, footerProps } = useCard(); @@ -61,7 +69,7 @@ export function SignUpContinue() {
- {firstNameEnabled && lastNameEnabled ? ( + {showFirstName && showLastName ? (
) : null} - {usernameEnabled ? ( + {showUserName ? ( ) : null} - {phoneNumberEnabled ? ( + {showPhoneNumber ? ( ) : null} - + {showEmail && } - {passwordEnabled && passwordRequired ? ( + {showPassword ? ( ) : null} - {legalConsentEnabled && } + {showLegalConsent && }
From c0a8b996f034286435a8709f485c5b6c9c363f77 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 15:48:24 +0200 Subject: [PATCH 06/11] Update packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts Co-authored-by: Tom Milewski --- .../src/internals/machines/sign-up/utils/fields-to-params.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts index d0a33b5bed..a4a6bb2b94 100644 --- a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts +++ b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts @@ -35,7 +35,6 @@ export function fieldsToSignUpParams Date: Wed, 30 Oct 2024 16:23:24 +0200 Subject: [PATCH 07/11] chore(elements): Expect type error --- .../internals/machines/sign-up/utils/fields-to-params.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts index a4a6bb2b94..ff4a4944d8 100644 --- a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts +++ b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts @@ -25,16 +25,17 @@ export function fieldsToSignUpParams { const params: SignUpUpdateParams = {}; - fields.forEach(({ value, checked, type }, key) => { + fields.forEach(({ value, checked }, key) => { if (isSignUpParam(key) && value !== undefined) { + // @ts-expect-error - Type is not narrowed to string params[key] = value as string; } - if (isSignUpParam(key) && type === 'checkbox') { + if (isSignUpParam(key) && checked !== undefined) { + // @ts-expect-error - Type is not narrowed to boolean params[key] = checked as boolean; } }); - return params; } From 3d579565ee711e4cb6cb495aca470e6669673fcf Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 18:56:10 +0200 Subject: [PATCH 08/11] fix(elements,ui): Distinct params types --- .../internals/machines/sign-up/utils/fields-to-params.ts | 6 +++--- packages/ui/src/common/legal-accepted.tsx | 2 -- packages/ui/src/primitives/card.tsx | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts index ff4a4944d8..80bfc16cac 100644 --- a/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts +++ b/packages/elements/src/internals/machines/sign-up/utils/fields-to-params.ts @@ -25,13 +25,13 @@ export function fieldsToSignUpParams { const params: SignUpUpdateParams = {}; - fields.forEach(({ value, checked }, key) => { - if (isSignUpParam(key) && value !== undefined) { + fields.forEach(({ value, checked, type }, key) => { + if (isSignUpParam(key) && value !== undefined && type !== 'checkbox') { // @ts-expect-error - Type is not narrowed to string params[key] = value as string; } - if (isSignUpParam(key) && checked !== undefined) { + if (isSignUpParam(key) && checked !== undefined && type === 'checkbox') { // @ts-expect-error - Type is not narrowed to boolean params[key] = checked as boolean; } diff --git a/packages/ui/src/common/legal-accepted.tsx b/packages/ui/src/common/legal-accepted.tsx index bc8fd89bf6..d95ca06923 100644 --- a/packages/ui/src/common/legal-accepted.tsx +++ b/packages/ui/src/common/legal-accepted.tsx @@ -1,5 +1,4 @@ import * as Common from '@clerk/elements/common'; -import { cx } from 'cva'; import React from 'react'; import { useAppearance } from '~/contexts'; @@ -47,7 +46,6 @@ export function LegalAcceptedField({ diff --git a/packages/ui/src/primitives/card.tsx b/packages/ui/src/primitives/card.tsx index ca184ab910..0854d22cba 100644 --- a/packages/ui/src/primitives/card.tsx +++ b/packages/ui/src/primitives/card.tsx @@ -309,7 +309,7 @@ export const Body = React.forwardRef Date: Wed, 30 Oct 2024 20:07:04 +0200 Subject: [PATCH 09/11] chore(repo): Update changeset --- .changeset/odd-peas-hear.md | 2 +- .changeset/witty-meals-retire.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .changeset/witty-meals-retire.md diff --git a/.changeset/odd-peas-hear.md b/.changeset/odd-peas-hear.md index 603f381a00..6ff76d69a1 100644 --- a/.changeset/odd-peas-hear.md +++ b/.changeset/odd-peas-hear.md @@ -2,4 +2,4 @@ "@clerk/elements": minor --- -Adding support for legal consent +Added support for `__experimental_legalAccepted` field diff --git a/.changeset/witty-meals-retire.md b/.changeset/witty-meals-retire.md new file mode 100644 index 0000000000..ba6502d60b --- /dev/null +++ b/.changeset/witty-meals-retire.md @@ -0,0 +1,8 @@ +--- +"@clerk/clerk-js": patch +"@clerk/types": patch +--- + +- Changed `__experimental_legalAccepted` checkbox Indicator element descriptor and element id +- Changed `__experimental_legalAccepted` checkbox Label element descriptor and element id +- Added two new element descriptors `formFieldCheckboxInput`, `formFieldCheckboxLabel`. From eadc5de3b92c9ce12854251a35206e5a63ec196f Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 30 Oct 2024 21:54:07 +0200 Subject: [PATCH 10/11] chore(element): Rename continue guards --- .../src/internals/machines/sign-up/continue.machine.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/elements/src/internals/machines/sign-up/continue.machine.ts b/packages/elements/src/internals/machines/sign-up/continue.machine.ts index 40cdc0a600..62cdeb3eea 100644 --- a/packages/elements/src/internals/machines/sign-up/continue.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/continue.machine.ts @@ -63,9 +63,9 @@ export const SignUpContinueMachine = setup({ sendToLoading, }, guards: { - isMissingRequirements: ({ context }) => + isStatusMissingRequirements: ({ context }) => context.parent.getSnapshot().context.clerk?.client?.signUp?.status === 'missing_requirements', - requirementsMet: ({ context }) => { + hasMetPreviousMissingRequirements: ({ context }) => { const signUp = context.parent.getSnapshot().context.clerk.client.signUp; const fields = context.formRef.getSnapshot().context.fields; @@ -97,7 +97,7 @@ export const SignUpContinueMachine = setup({ description: 'Waiting for user input', on: { SUBMIT: { - guard: or(['requirementsMet', not('isMissingRequirements')]), + guard: or(['hasMetPreviousMissingRequirements', not('isStatusMissingRequirements')]), target: 'Attempting', reenter: true, }, From d8648b8c955a479d9ea9293380bdb8255d239ca8 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Thu, 31 Oct 2024 13:12:11 +0200 Subject: [PATCH 11/11] fix(ui): Fix conditional for legal checkbox on Sign Up start --- packages/ui/src/components/sign-up/steps/start.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/sign-up/steps/start.tsx b/packages/ui/src/components/sign-up/steps/start.tsx index 576ffe7666..aa31645073 100644 --- a/packages/ui/src/components/sign-up/steps/start.tsx +++ b/packages/ui/src/components/sign-up/steps/start.tsx @@ -146,7 +146,7 @@ export function SignUpStart() { {options.socialButtonsPlacement === 'bottom' ? connectionsWithSeperator.reverse() : null} - {!(hasConnection && hasIdentifier && legalConsentEnabled) && } + {legalConsentEnabled && hasConnection && !hasIdentifier && } {userSettings.signUp.captcha_enabled ? : null}