diff --git a/.changeset/tender-badgers-rule.md b/.changeset/tender-badgers-rule.md new file mode 100644 index 00000000000..c9c2990c560 --- /dev/null +++ b/.changeset/tender-badgers-rule.md @@ -0,0 +1,5 @@ +--- +"@clerk/elements": patch +--- + +Fixes a bug during a ticket-based sign-up where the form could not be submitted if additional fields were needed. diff --git a/packages/elements/src/internals/machines/form/form.machine.ts b/packages/elements/src/internals/machines/form/form.machine.ts index 25da38daae9..28393b59f1f 100644 --- a/packages/elements/src/internals/machines/form/form.machine.ts +++ b/packages/elements/src/internals/machines/form/form.machine.ts @@ -169,7 +169,7 @@ export const FormMachine = setup({ if (field) { field.checked = event.field.checked; - field.disabled = event.field.disabled || false; + field.disabled = event.field.disabled ?? field.disabled; field.value = event.field.value; context.fields.set(event.field.name, field); diff --git a/packages/elements/src/internals/machines/shared/shared.actions.ts b/packages/elements/src/internals/machines/shared/shared.actions.ts index 18ce93aa281..33fd00af02d 100644 --- a/packages/elements/src/internals/machines/shared/shared.actions.ts +++ b/packages/elements/src/internals/machines/shared/shared.actions.ts @@ -12,7 +12,7 @@ import type { SignUpContinueContext, SignUpContinueEvents, SignUpStartContext, - SignUpStartRedirectEvent, + SignUpStartEvents, SignUpVerificationContext, SignUpVerificationEvents, } from '~/internals/machines/sign-up'; @@ -33,7 +33,7 @@ type SendToLoadingProps = { | SignInVerificationEvents | SignInResetPasswordEvents | ThirdPartyMachineEvent - | SignUpStartRedirectEvent + | SignUpStartEvents | SignUpContinueEvents | SignUpVerificationEvents; }; diff --git a/packages/elements/src/internals/machines/shared/shared.types.ts b/packages/elements/src/internals/machines/shared/shared.types.ts index 22eac7d24e9..6add45dfe40 100644 --- a/packages/elements/src/internals/machines/shared/shared.types.ts +++ b/packages/elements/src/internals/machines/shared/shared.types.ts @@ -6,6 +6,9 @@ import type { SignInStrategy, } from '@clerk/types'; import type { SetRequired, Simplify } from 'type-fest'; +import type { ActorRefFrom } from 'xstate'; + +import type { FormMachine } from '../form'; export type WithClerk> = { clerk: LoadedClerk } & T; export type WithClient> = { client: LoadedClerk['client'] } & T; @@ -33,3 +36,5 @@ export type AuthenticateWithRedirectSamlParams = Simplify< // ================= Strategies ================= // export type SignInStrategyName = SignInStrategy | 'oauth' | 'web3'; + +export type SetFormEvent = { type: 'SET_FORM'; formRef: ActorRefFrom }; diff --git a/packages/elements/src/internals/machines/sign-up/router.machine.ts b/packages/elements/src/internals/machines/sign-up/router.machine.ts index 84cfe139ba6..9f18b4c7dcf 100644 --- a/packages/elements/src/internals/machines/sign-up/router.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/router.machine.ts @@ -343,8 +343,10 @@ export const SignUpRouterMachine = setup({ }, on: { 'RESET.STEP': { - target: 'Start', - reenter: true, + actions: enqueueActions(({ enqueue, context }) => { + enqueue('clearFormErrors'); + enqueue.sendTo('start', { type: 'SET_FORM', formRef: context.formRef }); + }), }, NEXT: [ { @@ -355,6 +357,7 @@ export const SignUpRouterMachine = setup({ guard: and(['hasTicket', 'statusNeedsContinue']), actions: { type: 'navigateInternal', params: { path: '/' } }, target: 'Start', + reenter: true, }, { guard: 'statusNeedsVerification', diff --git a/packages/elements/src/internals/machines/sign-up/start.machine.ts b/packages/elements/src/internals/machines/sign-up/start.machine.ts index 3bec675f0ea..6e31223798a 100644 --- a/packages/elements/src/internals/machines/sign-up/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/start.machine.ts @@ -1,9 +1,11 @@ import type { SignUpResource, Web3Strategy } from '@clerk/types'; -import { assertEvent, enqueueActions, fromPromise, not, sendTo, setup } from 'xstate'; +import type { DoneActorEvent } from 'xstate'; +import { and, assertEvent, assign, enqueueActions, fromPromise, not, sendTo, setup } from 'xstate'; import { SIGN_UP_DEFAULT_BASE_PATH } from '~/internals/constants'; import { ClerkElementsRuntimeError } from '~/internals/errors'; import type { FormFields } from '~/internals/machines/form'; +import type { SetFormEvent } from '~/internals/machines/shared'; import { sendToLoading } from '~/internals/machines/shared'; import { fieldsToSignUpParams } from '~/internals/machines/sign-up/utils'; import { ThirdPartyMachine } from '~/internals/machines/third-party'; @@ -48,8 +50,14 @@ export const SignUpStartMachine = setup({ thirdParty: ThirdPartyMachine, }, actions: { - sendToNext: ({ context }) => context.parent.send({ type: 'NEXT' }), + sendToNext: ({ context, event }) => + context.parent.send({ type: 'NEXT', resource: (event as unknown as DoneActorEvent).output }), sendToLoading, + setFormRef: assign(({ event }) => { + return { + formRef: (event as unknown as SetFormEvent).formRef, + }; + }), setFormDisabledTicketFields: enqueueActions(({ context, enqueue }) => { if (!context.ticket) { return; @@ -90,6 +98,8 @@ export const SignUpStartMachine = setup({ }, }, guards: { + isMissingRequirements: ({ context }) => + context.parent.getSnapshot().context.clerk?.client?.signUp?.status === 'missing_requirements', hasTicket: ({ context }) => Boolean(context.ticket), isExampleMode: ({ context }) => Boolean(context.parent.getSnapshot().context.exampleMode), }, @@ -105,11 +115,20 @@ export const SignUpStartMachine = setup({ }), entry: 'setDefaultFormValues', initial: 'Init', + on: { + SET_FORM: { + actions: 'setFormRef', + }, + }, states: { Init: { description: 'Handle ticket, if present; Else, default to Pending state. Per tickets, `Attempting` makes a `signUp.create` request allowing for an incomplete sign up to contain progressively filled fields on the Start step.', always: [ + { + guard: and(['hasTicket', 'isMissingRequirements']), + target: 'Pending', + }, { guard: 'hasTicket', target: 'Attempting', diff --git a/packages/elements/src/internals/machines/sign-up/start.types.ts b/packages/elements/src/internals/machines/sign-up/start.types.ts index 0cf78efdc66..435db4cd3df 100644 --- a/packages/elements/src/internals/machines/sign-up/start.types.ts +++ b/packages/elements/src/internals/machines/sign-up/start.types.ts @@ -4,6 +4,7 @@ import type { ActorRefFrom, ErrorActorEvent } from 'xstate'; import type { FormMachine } from '~/internals/machines/form'; +import type { SetFormEvent } from '../shared'; import type { SignInRouterMachineActorRef } from './router.types'; // ---------------------------------- Tags ---------------------------------- // @@ -24,7 +25,7 @@ export type SignUpStartRedirectEvent = | SignUpStartRedirectSamlEvent | SignUpStartRedirectWeb3Event; -export type SignUpStartEvents = ErrorActorEvent | SignUpStartSubmitEvent | SignUpStartRedirectEvent; +export type SignUpStartEvents = ErrorActorEvent | SignUpStartSubmitEvent | SignUpStartRedirectEvent | SetFormEvent; // ---------------------------------- Input ---------------------------------- // diff --git a/packages/elements/src/react/sign-up/context/router.context.ts b/packages/elements/src/react/sign-up/context/router.context.ts index 76d105256dc..0f347d98c84 100644 --- a/packages/elements/src/react/sign-up/context/router.context.ts +++ b/packages/elements/src/react/sign-up/context/router.context.ts @@ -1,10 +1,10 @@ import type { ActorRefFrom, AnyActorRef, AnyStateMachine, SnapshotFrom } from 'xstate'; -import type { - TSignUpContinueMachine, - TSignUpRouterMachine, - TSignUpStartMachine, - TSignUpVerificationMachine, +import { + type TSignUpContinueMachine, + type TSignUpRouterMachine, + type TSignUpStartMachine, + type TSignUpVerificationMachine, } from '~/internals/machines/sign-up'; import { createContextFromActorRef } from '~/react/utils/create-context-from-actor-ref';