From 4cf64d2df6b76e69380e25e879609131b11d87e4 Mon Sep 17 00:00:00 2001 From: Steve Jensen Date: Wed, 12 Aug 2026 16:44:11 -0600 Subject: [PATCH 1/2] refactor: replace robot3/react-robot with local useReducer-based state machine Replace the external robot3 (v1.2.0) and react-robot (v1.2.1) dependencies with a local state machine library built on React's useReducer. This resolves machine lifecycle issues causing partner bugs (AMEX, BQE) where robot3's interpreter mishandled machine identity changes and silently swallowed events. The new implementation at src/lib/state-machine/ provides the same DSL (state, transition, guard, reduce, createMachine) with: - useReducer as the React integration (no mutable service objects) - Dev-mode console.warn for unhandled events (robot3 swallowed silently) - Clean re-initialization when machine reference changes - Zero external dependencies Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 30 --- package.json | 2 - .../contractorProfileStateMachine.ts | 2 +- .../ContractorProfile/index.tsx | 2 +- .../breadcrumbTransitionHelpers.ts | 8 +- .../Company/BankAccount/BankAccount.tsx | 2 +- .../Company/BankAccount/stateMachine.ts | 2 +- .../Company/DocumentSigner/DocumentSigner.tsx | 2 +- .../DocumentSigner/assignSignatoryState.tsx | 2 +- .../Company/DocumentSigner/stateMachine.ts | 2 +- .../Company/Locations/Locations.tsx | 2 +- .../Company/Locations/stateMachine.ts | 2 +- .../Company/OnboardingFlow/OnboardingFlow.tsx | 2 +- .../OnboardingFlow/onboardingStateMachine.ts | 2 +- .../Company/PaySchedule/PaySchedule.tsx | 2 +- .../PaySchedule/payScheduleStateMachine.ts | 2 +- .../Company/StateTaxes/StateTaxes.tsx | 2 +- .../StateTaxes/stateTaxesStateMachine.tsx | 2 +- .../Contractor/Address/management/Address.tsx | 2 +- .../Address/management/addressStateMachine.ts | 2 +- .../Compensation/management/Compensation.tsx | 2 +- .../management/compensationStateMachine.ts | 2 +- .../Contractor/Dashboard/DashboardFlow.tsx | 2 +- .../Dashboard/dashboardStateMachine.ts | 2 +- .../DocumentSigner/DocumentSigner.tsx | 2 +- .../onboarding/DocumentSigner/stateMachine.ts | 2 +- .../OnboardingFlow/OnboardingFlow.tsx | 2 +- .../onboardingStateMachine.test.ts | 2 +- .../OnboardingFlow/onboardingStateMachine.ts | 2 +- .../management/PaymentMethod.tsx | 2 +- .../management/paymentMethodStateMachine.ts | 2 +- .../CreatePaymentFlow/CreatePaymentFlow.tsx | 2 +- .../createPaymentMachine.test.ts | 2 +- .../CreatePaymentFlow/createPaymentMachine.ts | 2 +- .../Payments/PaymentFlow/PaymentFlow.tsx | 2 +- .../PaymentFlow/paymentStateMachine.test.ts | 2 +- .../PaymentFlow/paymentStateMachine.ts | 2 +- .../ViewPaymentFlow/ViewPaymentFlow.tsx | 2 +- .../viewPaymentMachine.test.ts | 2 +- .../ViewPaymentFlow/viewPaymentMachine.ts | 2 +- .../Contractor/Profile/management/Profile.tsx | 2 +- .../Profile/management/profileStateMachine.ts | 2 +- .../SelfOnboardingFlow/SelfOnboardingFlow.tsx | 2 +- .../selfOnboardingMachine.ts | 2 +- .../Compensation/management/Compensation.tsx | 2 +- .../management/compensationStateMachine.ts | 2 +- .../onboarding/Compensation.test.tsx | 2 +- .../Compensation/onboarding/Compensation.tsx | 2 +- .../compensationStateMachine.test.ts | 6 +- .../onboarding/compensationStateMachine.ts | 4 +- .../Employee/Dashboard/DashboardFlow.tsx | 2 +- .../Dashboard/dashboardStateMachine.test.ts | 2 +- .../Dashboard/dashboardStateMachine.ts | 2 +- .../Deductions/management/Deductions.tsx | 2 +- .../management/deductionsStateMachine.ts | 2 +- .../Deductions/onboarding/Deductions.tsx | 2 +- .../onboarding/stateMachine.test.ts | 4 +- .../Deductions/onboarding/stateMachine.ts | 4 +- .../Documents/management/Documents.tsx | 2 +- .../management/documentsStateMachine.ts | 2 +- .../DocumentSigner/DocumentSigner.tsx | 2 +- .../DocumentSigner/stateMachine.test.ts | 4 +- .../onboarding/DocumentSigner/stateMachine.ts | 4 +- .../EmployeeListFlow/EmployeeListFlow.tsx | 2 +- .../employeeListStateMachine.ts | 2 +- .../FederalTaxes/management/FederalTaxes.tsx | 2 +- .../management/federalTaxesStateMachine.ts | 2 +- .../HomeAddress/management/HomeAddress.tsx | 2 +- .../management/homeAddressStateMachine.ts | 2 +- .../OnboardingExecutionFlow.tsx | 2 +- .../onboardingExecutionStateMachine.ts | 2 +- .../OnboardingFlow/OnboardingFlow.tsx | 2 +- .../OnboardingFlow/onboardingStateMachine.ts | 2 +- .../management/PaymentMethod.tsx | 2 +- .../management/paymentMethodStateMachine.ts | 2 +- .../onboarding/PaymentMethod.tsx | 2 +- .../onboarding/paymentMethodStateMachine.ts | 2 +- .../Employee/Profile/management/Profile.tsx | 2 +- .../Profile/management/profileStateMachine.ts | 2 +- .../SelfOnboardingFlow/SelfOnboardingFlow.tsx | 2 +- .../selfOnboardingMachine.ts | 2 +- .../StateTaxes/management/StateTaxes.tsx | 2 +- .../management/stateTaxesStateMachine.ts | 2 +- .../TerminationFlow/TerminationFlow.tsx | 2 +- .../terminationStateMachine.test.ts | 2 +- .../terminationStateMachine.ts | 2 +- .../WorkAddress/management/WorkAddress.tsx | 2 +- .../management/workAddressStateMachine.ts | 2 +- src/components/Flow/Flow.test.tsx | 2 +- src/components/Flow/Flow.tsx | 26 +- .../InformationRequests.tsx | 3 +- .../informationRequestsStateMachine.tsx | 2 +- .../ConfirmWireDetails/ConfirmWireDetails.tsx | 3 +- .../confirmWireDetailsStateMachine.tsx | 2 +- .../Payroll/Dismissal/DismissalFlow.tsx | 2 +- .../Dismissal/dismissalStateMachine.test.ts | 2 +- .../Dismissal/dismissalStateMachine.ts | 2 +- .../OffCycle/OffCycleExecution.test.tsx | 2 +- .../Payroll/OffCycle/OffCycleFlow.tsx | 2 +- .../OffCycle/offCycleStateMachine.test.ts | 2 +- .../Payroll/OffCycle/offCycleStateMachine.ts | 2 +- .../PayrollExecutionFlow.tsx | 2 +- .../payrollExecutionMachine.test.ts | 2 +- .../payrollExecutionMachine.ts | 2 +- .../Payroll/PayrollFlow/PayrollFlow.tsx | 2 +- .../PayrollFlow/payrollStateMachine.test.ts | 2 +- .../PayrollFlow/payrollStateMachine.ts | 2 +- .../Payroll/PayrollLanding/PayrollLanding.tsx | 2 +- .../payrollLandingStateMachine.ts | 2 +- .../Payroll/RecoveryCases/RecoveryCases.tsx | 3 +- .../recoveryCasesStateMachine.tsx | 2 +- .../Payroll/Transition/TransitionFlow.tsx | 2 +- .../Transition/transitionStateMachine.test.ts | 2 +- .../Transition/transitionStateMachine.ts | 2 +- .../TimeOff/TimeOffFlow/TimeOffFlow.tsx | 2 +- .../TimeOffFlow/timeOffStateMachine.test.ts | 2 +- .../TimeOffFlow/timeOffStateMachine.ts | 2 +- src/lib/state-machine/__tests__/core.test.ts | 246 ++++++++++++++++++ .../__tests__/useMachine.test.tsx | 161 ++++++++++++ src/lib/state-machine/core.ts | 140 ++++++++++ src/lib/state-machine/index.ts | 75 ++++++ src/lib/state-machine/types.ts | 66 +++++ src/shared/constants.ts | 2 +- src/types/Helpers.d.ts | 4 +- 124 files changed, 821 insertions(+), 182 deletions(-) create mode 100644 src/lib/state-machine/__tests__/core.test.ts create mode 100644 src/lib/state-machine/__tests__/useMachine.test.tsx create mode 100644 src/lib/state-machine/core.ts create mode 100644 src/lib/state-machine/index.ts create mode 100644 src/lib/state-machine/types.ts diff --git a/package-lock.json b/package-lock.json index 78656f7818..5a85b28e5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,6 @@ "react-error-boundary": "^6.1.3", "react-hook-form": "^7.85.0", "react-i18next": "^17.0.11", - "react-robot": "^1.2.1", - "robot3": "^1.2.0", "zod": "^4.3.6" }, "devDependencies": { @@ -23239,19 +23237,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-robot": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/react-robot/-/react-robot-1.2.1.tgz", - "integrity": "sha512-FGX25Z1VFLxB05uFLRqg+MUqtcv1ZiugNjdIu35EYcJ0zDoc40H6yKi4VrEq+v49YTAa14khgTzRb+IM8YG/LA==", - "license": "BSD-2-Clause", - "dependencies": { - "robot-hooks": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18.2.0 || ^19.0.0", - "robot3": "^1.0.0" - } - }, "node_modules/react-router": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", @@ -23851,21 +23836,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/robot-hooks": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/robot-hooks/-/robot-hooks-1.1.0.tgz", - "integrity": "sha512-42LgnUjW0gL0CZw9v9GJz+JtjAG4L+IluuhlA0jRgmYH1L+JqcyKKRQ4RSrZgyFJcStOYPKSqd5GneCDkdzZhw==", - "license": "BSD-2-Clause", - "peerDependencies": { - "robot3": "^1.0.2" - } - }, - "node_modules/robot3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/robot3/-/robot3-1.2.0.tgz", - "integrity": "sha512-Xin8KHqCKrD9Rqk1ZzZQYjsb6S9DRggcfwBqnVPeM3DLtNCJLxWWTrPJDYm3E+ZiTO7H3VMdgyPSkIbuYnYP2Q==", - "license": "BSD-2-Clause" - }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", diff --git a/package.json b/package.json index 2139e2bcba..8342ec0259 100644 --- a/package.json +++ b/package.json @@ -198,8 +198,6 @@ "react-error-boundary": "^6.1.3", "react-hook-form": "^7.85.0", "react-i18next": "^17.0.11", - "react-robot": "^1.2.1", - "robot3": "^1.2.0", "zod": "^4.3.6" }, "sideEffects": [ diff --git a/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/contractorProfileStateMachine.ts b/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/contractorProfileStateMachine.ts index 3b5aae91f3..ce79d41b6a 100644 --- a/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/contractorProfileStateMachine.ts +++ b/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/contractorProfileStateMachine.ts @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { ContractorProfileContextInterface } from './ContractorProfileComponents' import { @@ -9,6 +8,7 @@ import { EditCompensationContextual, EditBasicDetailsContextual, } from './ContractorProfileComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/index.tsx b/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/index.tsx index 7e98ca9cf1..8a934c8d24 100644 --- a/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/index.tsx +++ b/sdk-app/src/design/prototypes/contractor-management/ContractorProfile/index.tsx @@ -1,10 +1,10 @@ import { useMemo } from 'react' import { useOutletContext, useParams } from 'react-router-dom' -import { createMachine } from 'robot3' import type { EntityIds } from '../../../../useEntities' import { contractorProfileStateMachine } from './contractorProfileStateMachine' import type { ContractorProfileContextInterface } from './ContractorProfileComponents' import { ProfileViewContextual } from './ContractorProfileComponents' +import { createMachine } from '@/lib/state-machine' import { EmptyData } from '@/components/Common' import { Flow } from '@/components/Flow/Flow' import { BaseComponent, useBase } from '@/components/Base' diff --git a/src/components/Common/FlowBreadcrumbs/breadcrumbTransitionHelpers.ts b/src/components/Common/FlowBreadcrumbs/breadcrumbTransitionHelpers.ts index bd3afb2ad8..aa7921f639 100644 --- a/src/components/Common/FlowBreadcrumbs/breadcrumbTransitionHelpers.ts +++ b/src/components/Common/FlowBreadcrumbs/breadcrumbTransitionHelpers.ts @@ -1,4 +1,4 @@ -import { guard, reduce, transition } from 'robot3' +import { guard, reduce, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' type BreadcrumbNavigateEvent = { @@ -8,9 +8,8 @@ type BreadcrumbNavigateEvent = { } } -/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */ /** - * Builds a factory that produces robot3 transitions handling a + * Builds a factory that produces transitions handling a * `breadcrumb/navigate` event for a given target state. * * @remarks @@ -22,11 +21,10 @@ type BreadcrumbNavigateEvent = { * * @typeParam TContext - The state machine context shape. * @returns A factory `(targetState, canNavigate?) => transition` that emits a - * guarded robot3 transition for the `breadcrumb/navigate` event. + * guarded transition for the `breadcrumb/navigate` event. * @internal */ export const createBreadcrumbNavigateTransition = () => { - /* eslint-enable @typescript-eslint/no-unnecessary-type-parameters */ return (targetState: string, canNavigate?: (ctx: TContext) => boolean) => transition( componentEvents.BREADCRUMB_NAVIGATE, diff --git a/src/components/Company/BankAccount/BankAccount.tsx b/src/components/Company/BankAccount/BankAccount.tsx index 1e1e4ee36b..cc5ab914ec 100644 --- a/src/components/Company/BankAccount/BankAccount.tsx +++ b/src/components/Company/BankAccount/BankAccount.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { useBankAccountsGetSuspense } from '@gusto/embedded-api/react-query/bankAccountsGet' import { @@ -7,6 +6,7 @@ import { } from './BankAccountComponents' import { bankAccountStateMachine } from './stateMachine' import { BankAccountListContextual } from './BankAccountComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseComponent, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Company/BankAccount/stateMachine.ts b/src/components/Company/BankAccount/stateMachine.ts index 6f4729484e..73bf0c137f 100644 --- a/src/components/Company/BankAccount/stateMachine.ts +++ b/src/components/Company/BankAccount/stateMachine.ts @@ -1,10 +1,10 @@ -import { state, transition, reduce } from 'robot3' import type { BankAccountContextInterface, EventPayloads } from './BankAccountComponents' import { BankAccountFormContextual, BankAccountVerifyContextual, BankAccountListContextual, } from './BankAccountComponents' +import { state, transition, reduce } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Company/DocumentSigner/DocumentSigner.tsx b/src/components/Company/DocumentSigner/DocumentSigner.tsx index 5b8b324bbb..84f8584d28 100644 --- a/src/components/Company/DocumentSigner/DocumentSigner.tsx +++ b/src/components/Company/DocumentSigner/DocumentSigner.tsx @@ -1,9 +1,9 @@ -import { createMachine } from 'robot3' import { useSignatoriesListSuspense } from '@gusto/embedded-api/react-query/signatoriesList' import { useMemo } from 'react' import { AssignSignatory, DocumentList } from './documentSignerStateMachine' import { documentSignerMachine } from './stateMachine' import type { DocumentSignerContextInterface } from './useDocumentSigner' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseComponent, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Company/DocumentSigner/assignSignatoryState.tsx b/src/components/Company/DocumentSigner/assignSignatoryState.tsx index ea4524914e..ca283ade1c 100644 --- a/src/components/Company/DocumentSigner/assignSignatoryState.tsx +++ b/src/components/Company/DocumentSigner/assignSignatoryState.tsx @@ -1,7 +1,7 @@ -import { state, transition, reduce } from 'robot3' import type { EventPayloads } from './documentSignerStateMachine' import { DocumentList } from './documentSignerStateMachine' import type { DocumentSignerContextInterface } from './useDocumentSigner' +import { state, transition, reduce } from '@/lib/state-machine' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { companyEvents } from '@/shared/constants' diff --git a/src/components/Company/DocumentSigner/stateMachine.ts b/src/components/Company/DocumentSigner/stateMachine.ts index cf77279a1a..af8de89246 100644 --- a/src/components/Company/DocumentSigner/stateMachine.ts +++ b/src/components/Company/DocumentSigner/stateMachine.ts @@ -1,8 +1,8 @@ -import { state, transition, reduce } from 'robot3' import type { EventPayloads } from './documentSignerStateMachine' import { SignatureForm, AssignSignatory, DocumentList } from './documentSignerStateMachine' import { assignSignatoryState } from './assignSignatoryState' import type { DocumentSignerContextInterface } from './useDocumentSigner' +import { state, transition, reduce } from '@/lib/state-machine' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { companyEvents } from '@/shared/constants' diff --git a/src/components/Company/Locations/Locations.tsx b/src/components/Company/Locations/Locations.tsx index 055e945c35..004f0753ab 100644 --- a/src/components/Company/Locations/Locations.tsx +++ b/src/components/Company/Locations/Locations.tsx @@ -1,8 +1,8 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { type LocationsContextInterface } from './locationsStateMachine' import { locationsStateMachine } from './stateMachine' import { LocationsListContextual } from './locationsStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Company/Locations/stateMachine.ts b/src/components/Company/Locations/stateMachine.ts index 61f58c0ae8..cfaad90950 100644 --- a/src/components/Company/Locations/stateMachine.ts +++ b/src/components/Company/Locations/stateMachine.ts @@ -1,6 +1,6 @@ -import { state, transition, reduce } from 'robot3' import type { LocationsContextInterface, EventPayloads } from './locationsStateMachine' import { LocationFormContextual, LocationsListContextual } from './locationsStateMachine' +import { state, transition, reduce } from '@/lib/state-machine' import { companyEvents, componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Company/OnboardingFlow/OnboardingFlow.tsx b/src/components/Company/OnboardingFlow/OnboardingFlow.tsx index 86de230a20..8a546aceb4 100644 --- a/src/components/Company/OnboardingFlow/OnboardingFlow.tsx +++ b/src/components/Company/OnboardingFlow/OnboardingFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { onboardingMachine } from './onboardingStateMachine' import type { OnboardingFlowProps } from './OnboardingFlowComponents' @@ -6,6 +5,7 @@ import { OnboardingOverviewContextual, type OnboardingFlowContextInterface, } from './OnboardingFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' export type { OnboardingFlowProps, OnboardingFlowDefaultValues } from './OnboardingFlowComponents' diff --git a/src/components/Company/OnboardingFlow/onboardingStateMachine.ts b/src/components/Company/OnboardingFlow/onboardingStateMachine.ts index cf9ba6f984..ce0328cf55 100644 --- a/src/components/Company/OnboardingFlow/onboardingStateMachine.ts +++ b/src/components/Company/OnboardingFlow/onboardingStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state } from 'robot3' import { BankAccountContextual, DocumentSignerContextual, @@ -11,6 +10,7 @@ import { StateTaxesContextual, type OnboardingFlowContextInterface, } from './OnboardingFlowComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' import type { FlowHeaderConfig } from '@/components/Flow/useFlow' diff --git a/src/components/Company/PaySchedule/PaySchedule.tsx b/src/components/Company/PaySchedule/PaySchedule.tsx index ce7d63b012..8e6e7a7479 100644 --- a/src/components/Company/PaySchedule/PaySchedule.tsx +++ b/src/components/Company/PaySchedule/PaySchedule.tsx @@ -1,10 +1,10 @@ import { useMemo } from 'react' -import { createMachine } from 'robot3' import { usePaySchedulesGetAllSuspense } from '@gusto/embedded-api/react-query/paySchedulesGetAll' import { payScheduleStateMachine } from './payScheduleStateMachine' import type { PayScheduleContextInterface } from './PayScheduleComponents' import { PayScheduleFormContextual, PayScheduleListContextual } from './PayScheduleComponents' import type { PayScheduleFormData } from './shared/usePayScheduleForm/payScheduleSchema' +import { createMachine } from '@/lib/state-machine' import type { BaseComponentInterface } from '@/components/Base' import { BaseComponent, useBase } from '@/components/Base' import type { BaseComponentKeys } from '@/components/Base/Base' diff --git a/src/components/Company/PaySchedule/payScheduleStateMachine.ts b/src/components/Company/PaySchedule/payScheduleStateMachine.ts index f106cfbcc0..705b601086 100644 --- a/src/components/Company/PaySchedule/payScheduleStateMachine.ts +++ b/src/components/Company/PaySchedule/payScheduleStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { PayScheduleContextInterface } from './PayScheduleComponents' import { PayScheduleFormContextual, PayScheduleListContextual } from './PayScheduleComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Company/StateTaxes/StateTaxes.tsx b/src/components/Company/StateTaxes/StateTaxes.tsx index 0ca9415f19..9fcba1cfd5 100644 --- a/src/components/Company/StateTaxes/StateTaxes.tsx +++ b/src/components/Company/StateTaxes/StateTaxes.tsx @@ -1,8 +1,8 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { stateTaxesStateMachine } from './stateTaxesStateMachine' import type { StateTaxesContextInterface } from './StateTaxesComponents' import { StateTaxesListContextual } from './StateTaxesComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Company/StateTaxes/stateTaxesStateMachine.tsx b/src/components/Company/StateTaxes/stateTaxesStateMachine.tsx index 6bdc915aa9..904a72387b 100644 --- a/src/components/Company/StateTaxes/stateTaxesStateMachine.tsx +++ b/src/components/Company/StateTaxes/stateTaxesStateMachine.tsx @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { StateTaxesContextInterface } from './StateTaxesComponents' import { @@ -6,6 +5,7 @@ import { StateTaxesListContextual, TaxRateManagementContextual, } from './StateTaxesComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/Address/management/Address.tsx b/src/components/Contractor/Address/management/Address.tsx index 3ff694483a..2b6e82017e 100644 --- a/src/components/Contractor/Address/management/Address.tsx +++ b/src/components/Contractor/Address/management/Address.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CardContextual, type AddressContextInterface } from './AddressComponents' import { addressStateMachine } from './addressStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Contractor/Address/management/addressStateMachine.ts b/src/components/Contractor/Address/management/addressStateMachine.ts index 7672e0bf6b..d69370ff8d 100644 --- a/src/components/Contractor/Address/management/addressStateMachine.ts +++ b/src/components/Contractor/Address/management/addressStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { AddressContextInterface } from './AddressComponents' import { CardContextual, AddressEditFormContextual } from './AddressComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/Compensation/management/Compensation.tsx b/src/components/Contractor/Compensation/management/Compensation.tsx index e20a9f4fba..7c393f575d 100644 --- a/src/components/Contractor/Compensation/management/Compensation.tsx +++ b/src/components/Contractor/Compensation/management/Compensation.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CardContextual, type CompensationContextInterface } from './CompensationComponents' import { compensationStateMachine } from './compensationStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Contractor/Compensation/management/compensationStateMachine.ts b/src/components/Contractor/Compensation/management/compensationStateMachine.ts index 49eb6b5335..1a87089b87 100644 --- a/src/components/Contractor/Compensation/management/compensationStateMachine.ts +++ b/src/components/Contractor/Compensation/management/compensationStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { CompensationContextInterface } from './CompensationComponents' import { CardContextual, CompensationEditFormContextual } from './CompensationComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/Dashboard/DashboardFlow.tsx b/src/components/Contractor/Dashboard/DashboardFlow.tsx index 037330d9a9..ff2a0ac7a5 100644 --- a/src/components/Contractor/Dashboard/DashboardFlow.tsx +++ b/src/components/Contractor/Dashboard/DashboardFlow.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { dashboardStateMachine } from './dashboardStateMachine' import { type DashboardContextInterface, DashboardViewContextual } from './DashboardComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { BaseComponentInterface } from '@/components/Base' diff --git a/src/components/Contractor/Dashboard/dashboardStateMachine.ts b/src/components/Contractor/Dashboard/dashboardStateMachine.ts index 096e013192..4f5bd1b56a 100644 --- a/src/components/Contractor/Dashboard/dashboardStateMachine.ts +++ b/src/components/Contractor/Dashboard/dashboardStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state } from 'robot3' import type { DashboardTab } from './Dashboard' import { DashboardViewContextual, @@ -8,6 +7,7 @@ import { CompensationEditFormContextual, type DashboardContextInterface, } from './DashboardComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/Documents/onboarding/DocumentSigner/DocumentSigner.tsx b/src/components/Contractor/Documents/onboarding/DocumentSigner/DocumentSigner.tsx index 4d40252194..2b93079120 100644 --- a/src/components/Contractor/Documents/onboarding/DocumentSigner/DocumentSigner.tsx +++ b/src/components/Contractor/Documents/onboarding/DocumentSigner/DocumentSigner.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import type { DocumentsListProps } from '../DocumentsList' import { @@ -6,6 +5,7 @@ import { type DocumentSignerContextInterface, } from './documentSignerStateMachine' import { documentSignerMachine } from './stateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseComponent, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Contractor/Documents/onboarding/DocumentSigner/stateMachine.ts b/src/components/Contractor/Documents/onboarding/DocumentSigner/stateMachine.ts index de5477b673..485d9de59d 100644 --- a/src/components/Contractor/Documents/onboarding/DocumentSigner/stateMachine.ts +++ b/src/components/Contractor/Documents/onboarding/DocumentSigner/stateMachine.ts @@ -1,6 +1,6 @@ -import { state, transition, reduce } from 'robot3' import type { DocumentSignerContextInterface, EventPayloads } from './documentSignerStateMachine' import { SignatureFormContextual, DocumentsListContextual } from './documentSignerStateMachine' +import { state, transition, reduce } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/OnboardingFlow/OnboardingFlow.tsx b/src/components/Contractor/OnboardingFlow/OnboardingFlow.tsx index 81f31edffd..b0e482ac77 100644 --- a/src/components/Contractor/OnboardingFlow/OnboardingFlow.tsx +++ b/src/components/Contractor/OnboardingFlow/OnboardingFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { onboardingMachine } from './onboardingStateMachine' import type { OnboardingFlowProps } from './OnboardingFlowComponents' @@ -6,6 +5,7 @@ import { ContractorListContextual, type OnboardingFlowContextInterface, } from './OnboardingFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' /** diff --git a/src/components/Contractor/OnboardingFlow/onboardingStateMachine.test.ts b/src/components/Contractor/OnboardingFlow/onboardingStateMachine.test.ts index ae60bcf777..9902b52e44 100644 --- a/src/components/Contractor/OnboardingFlow/onboardingStateMachine.test.ts +++ b/src/components/Contractor/OnboardingFlow/onboardingStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret } from 'robot3' +import { createMachine, interpret } from '@/lib/state-machine' import { onboardingMachine } from './onboardingStateMachine' import { ContractorListContextual, diff --git a/src/components/Contractor/OnboardingFlow/onboardingStateMachine.ts b/src/components/Contractor/OnboardingFlow/onboardingStateMachine.ts index 118e105389..436cdf63fc 100644 --- a/src/components/Contractor/OnboardingFlow/onboardingStateMachine.ts +++ b/src/components/Contractor/OnboardingFlow/onboardingStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state, guard } from 'robot3' import type { Contractor } from '@gusto/embedded-api/models/components/contractor' import { AddressContextual, @@ -10,6 +9,7 @@ import { SubmitContextual, type OnboardingFlowContextInterface, } from './OnboardingFlowComponents' +import { transition, reduce, state, guard } from '@/lib/state-machine' import { componentEvents, ContractorOnboardingStatus } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import type { FlowHeaderConfig } from '@/components/Flow/useFlow' diff --git a/src/components/Contractor/PaymentMethod/management/PaymentMethod.tsx b/src/components/Contractor/PaymentMethod/management/PaymentMethod.tsx index d4f29cc6ee..ad1d6062ab 100644 --- a/src/components/Contractor/PaymentMethod/management/PaymentMethod.tsx +++ b/src/components/Contractor/PaymentMethod/management/PaymentMethod.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CardContextual, type PaymentMethodContextInterface } from './PaymentMethodComponents' import { paymentMethodStateMachine } from './paymentMethodStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Contractor/PaymentMethod/management/paymentMethodStateMachine.ts b/src/components/Contractor/PaymentMethod/management/paymentMethodStateMachine.ts index 16baad0a07..eabf64595e 100644 --- a/src/components/Contractor/PaymentMethod/management/paymentMethodStateMachine.ts +++ b/src/components/Contractor/PaymentMethod/management/paymentMethodStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { PaymentMethodContextInterface } from './PaymentMethodComponents' import { CardContextual, PaymentMethodEditFormContextual } from './PaymentMethodComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/Payments/CreatePaymentFlow/CreatePaymentFlow.tsx b/src/components/Contractor/Payments/CreatePaymentFlow/CreatePaymentFlow.tsx index bdbe7061fe..fbc5eeb4a2 100644 --- a/src/components/Contractor/Payments/CreatePaymentFlow/CreatePaymentFlow.tsx +++ b/src/components/Contractor/Payments/CreatePaymentFlow/CreatePaymentFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useState } from 'react' import { createPaymentBreadcrumbsNodes, createPaymentMachine } from './createPaymentMachine' import { @@ -6,6 +5,7 @@ import { type CreatePaymentFlowContextInterface, type CreatePaymentFlowProps, } from './CreatePaymentFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { FlowBreadcrumb } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' import { buildBreadcrumbs, updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.test.ts b/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.test.ts index 165a6aef6b..3914944241 100644 --- a/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.test.ts +++ b/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { createPaymentMachine, createPaymentBreadcrumbsNodes } from './createPaymentMachine' import type { CreatePaymentFlowContextInterface } from './CreatePaymentFlowComponents' import { componentEvents, payrollWireEvents } from '@/shared/constants' diff --git a/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.ts b/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.ts index e7c3a6a1c7..62032cfc5a 100644 --- a/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.ts +++ b/src/components/Contractor/Payments/CreatePaymentFlow/createPaymentMachine.ts @@ -1,10 +1,10 @@ -import { reduce, state, transition } from 'robot3' import type { ContractorPaymentGroup } from '@gusto/embedded-api/models/components/contractorpaymentgroup' import type { WireInRequest } from '@gusto/embedded-api/models/components/wireinrequest' import { PaymentSummaryContextual, type CreatePaymentFlowContextInterface, } from './CreatePaymentFlowComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { payrollWireEvents, componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Payments/PaymentFlow/PaymentFlow.tsx b/src/components/Contractor/Payments/PaymentFlow/PaymentFlow.tsx index fca997925c..a240fb708f 100644 --- a/src/components/Contractor/Payments/PaymentFlow/PaymentFlow.tsx +++ b/src/components/Contractor/Payments/PaymentFlow/PaymentFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { paymentFlowBreadcrumbsNodes, paymentMachine } from './paymentStateMachine' import { @@ -6,6 +5,7 @@ import { type PaymentFlowContextInterface, type PaymentFlowProps, } from './PaymentFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.test.ts b/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.test.ts index 51912663ea..245c99615a 100644 --- a/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.test.ts +++ b/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { paymentMachine, paymentFlowBreadcrumbsNodes } from './paymentStateMachine' import type { PaymentFlowContextInterface } from './PaymentFlowComponents' import { componentEvents, informationRequestEvents, payrollWireEvents } from '@/shared/constants' diff --git a/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.ts b/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.ts index ceee4445e7..b78769096d 100644 --- a/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.ts +++ b/src/components/Contractor/Payments/PaymentFlow/paymentStateMachine.ts @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { WireInRequest } from '@gusto/embedded-api/models/components/wireinrequest' import { CreatePaymentFlowContextual, @@ -8,6 +7,7 @@ import { type PaymentFlowContextInterface, ViewPaymentFlowContextual, } from './PaymentFlowComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents, informationRequestEvents, payrollWireEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { patchBreadcrumbsHeader } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Payments/ViewPaymentFlow/ViewPaymentFlow.tsx b/src/components/Contractor/Payments/ViewPaymentFlow/ViewPaymentFlow.tsx index 5f229d00ac..87da268578 100644 --- a/src/components/Contractor/Payments/ViewPaymentFlow/ViewPaymentFlow.tsx +++ b/src/components/Contractor/Payments/ViewPaymentFlow/ViewPaymentFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useState } from 'react' import { viewPaymentBreadcrumbsNodes, viewPaymentMachine } from './viewPaymentMachine' import { @@ -6,6 +5,7 @@ import { type ViewPaymentFlowContextInterface, type ViewPaymentFlowProps, } from './ViewPaymentFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { FlowBreadcrumb } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' import { buildBreadcrumbs, updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.test.ts b/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.test.ts index d46b76a579..2f84ae5f64 100644 --- a/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.test.ts +++ b/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { viewPaymentMachine, viewPaymentBreadcrumbsNodes } from './viewPaymentMachine' import type { ViewPaymentFlowContextInterface } from './ViewPaymentFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.ts b/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.ts index fdd654bd42..0511f126e7 100644 --- a/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.ts +++ b/src/components/Contractor/Payments/ViewPaymentFlow/viewPaymentMachine.ts @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { Contractor } from '@gusto/embedded-api/models/components/contractor' import { getContractorDisplayName } from '../../shared/helpers' import { @@ -6,6 +5,7 @@ import { PaymentStatementContextual, type ViewPaymentFlowContextInterface, } from './ViewPaymentFlowComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Profile/management/Profile.tsx b/src/components/Contractor/Profile/management/Profile.tsx index 7f8b6f8214..2f32ac3c3c 100644 --- a/src/components/Contractor/Profile/management/Profile.tsx +++ b/src/components/Contractor/Profile/management/Profile.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CardContextual, type ProfileContextInterface } from './ProfileComponents' import { profileStateMachine } from './profileStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Contractor/Profile/management/profileStateMachine.ts b/src/components/Contractor/Profile/management/profileStateMachine.ts index 21aadcc5c0..d3ba25faa8 100644 --- a/src/components/Contractor/Profile/management/profileStateMachine.ts +++ b/src/components/Contractor/Profile/management/profileStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { ProfileContextInterface } from './ProfileComponents' import { CardContextual, ProfileEditFormContextual } from './ProfileComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Contractor/SelfOnboardingFlow/SelfOnboardingFlow.tsx b/src/components/Contractor/SelfOnboardingFlow/SelfOnboardingFlow.tsx index fcb52193d2..1cf5752466 100644 --- a/src/components/Contractor/SelfOnboardingFlow/SelfOnboardingFlow.tsx +++ b/src/components/Contractor/SelfOnboardingFlow/SelfOnboardingFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import type { SelfOnboardingContextInterface, @@ -6,6 +5,7 @@ import type { } from './SelfOnboardingComponents' import { Landing } from './SelfOnboardingComponents' import { contractorSelfOnboardingMachine } from './selfOnboardingMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' export type { SelfOnboardingFlowProps } from './SelfOnboardingComponents' diff --git a/src/components/Contractor/SelfOnboardingFlow/selfOnboardingMachine.ts b/src/components/Contractor/SelfOnboardingFlow/selfOnboardingMachine.ts index a67c280c86..733f91c273 100644 --- a/src/components/Contractor/SelfOnboardingFlow/selfOnboardingMachine.ts +++ b/src/components/Contractor/SelfOnboardingFlow/selfOnboardingMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state } from 'robot3' import type { SelfOnboardingContextInterface } from './SelfOnboardingComponents' import { Profile, @@ -7,6 +6,7 @@ import { DocumentSigner, OnboardingSummary, } from './SelfOnboardingComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Compensation/management/Compensation.tsx b/src/components/Employee/Compensation/management/Compensation.tsx index 216656bf2d..7c87c9fb1f 100644 --- a/src/components/Employee/Compensation/management/Compensation.tsx +++ b/src/components/Employee/Compensation/management/Compensation.tsx @@ -1,10 +1,10 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CompensationCardContextual, type CompensationContextInterface, } from './CompensationComponents' import { compensationStateMachine } from './compensationStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/Compensation/management/compensationStateMachine.ts b/src/components/Employee/Compensation/management/compensationStateMachine.ts index 977da646f6..5a3fa13a01 100644 --- a/src/components/Employee/Compensation/management/compensationStateMachine.ts +++ b/src/components/Employee/Compensation/management/compensationStateMachine.ts @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { CompensationContextInterface } from './CompensationComponents' import { @@ -7,6 +6,7 @@ import { CompensationAddJobFormContextual, CompensationAddAnotherJobFormContextual, } from './CompensationComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Compensation/onboarding/Compensation.test.tsx b/src/components/Employee/Compensation/onboarding/Compensation.test.tsx index 4bd40be010..be42ff35f2 100644 --- a/src/components/Employee/Compensation/onboarding/Compensation.test.tsx +++ b/src/components/Employee/Compensation/onboarding/Compensation.test.tsx @@ -504,7 +504,7 @@ describe('Compensation', () => { // Regression test for SDK-1169: after leaving and returning to a still-mounted // Compensation component, "Add new job"/"Edit" silently stopped navigating. Root - // cause: `done` was modeled as a robot3 final state (no transitions out), so a host + // cause: `done` was modeled as a final state (no transitions out), so a host // that doesn't unmount Compensation once `employee/compensation/done` fires was // left with a jobs list whose controls could never navigate again. The fix removes // the terminal state entirely — Flow re-emits the DONE event to the parent diff --git a/src/components/Employee/Compensation/onboarding/Compensation.tsx b/src/components/Employee/Compensation/onboarding/Compensation.tsx index 26c3e09456..53effa74fe 100644 --- a/src/components/Employee/Compensation/onboarding/Compensation.tsx +++ b/src/components/Employee/Compensation/onboarding/Compensation.tsx @@ -1,5 +1,4 @@ import { useMemo, useState } from 'react' -import { createMachine } from 'robot3' import { useTranslation } from 'react-i18next' import { useJobsAndCompensationsGetJobsSuspense } from '@gusto/embedded-api/react-query/jobsAndCompensationsGetJobs' import { type Job } from '@gusto/embedded-api/models/components/job' @@ -11,6 +10,7 @@ import { type CompensationFlowContextInterface, } from './CompensationFlowComponents' import { compensationStateMachine } from './compensationStateMachine' +import { createMachine } from '@/lib/state-machine' import type { RequireAtLeastOne } from '@/types/Helpers' import type { PAY_PERIODS } from '@/shared/constants' import { FlsaStatus } from '@/shared/constants' diff --git a/src/components/Employee/Compensation/onboarding/compensationStateMachine.test.ts b/src/components/Employee/Compensation/onboarding/compensationStateMachine.test.ts index fda7391602..243032a2d6 100644 --- a/src/components/Employee/Compensation/onboarding/compensationStateMachine.test.ts +++ b/src/components/Employee/Compensation/onboarding/compensationStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { compensationStateMachine } from './compensationStateMachine' import type { CompensationFlowContextInterface } from './CompensationFlowComponents' import { componentEvents } from '@/shared/constants' @@ -31,8 +31,8 @@ function send(service: ReturnType, type: string, payload?: describe('compensationStateMachine', () => { // Regression test for SDK-1169: Add new job/Edit silently stopped navigating after // leaving and returning to a still-mounted Compensation component. `done` used to be - // a robot3 final state (no transitions out), so once EMPLOYEE_COMPENSATION_DONE - // fired, every subsequent event was a documented robot3 no-op and the machine could + // a final state (no transitions out), so once EMPLOYEE_COMPENSATION_DONE + // fired, every subsequent event was a no-op and the machine could // never advance. EMPLOYEE_COMPENSATION_DONE now has no transition at all: the // machine stays in `viewJobs`, and Flow re-emits the event to the parent regardless. it('SDK-1169: keeps handling EMPLOYEE_JOB_ADD/EMPLOYEE_JOB_EDIT after DONE fires', () => { diff --git a/src/components/Employee/Compensation/onboarding/compensationStateMachine.ts b/src/components/Employee/Compensation/onboarding/compensationStateMachine.ts index 7ce1024406..2252c89253 100644 --- a/src/components/Employee/Compensation/onboarding/compensationStateMachine.ts +++ b/src/components/Employee/Compensation/onboarding/compensationStateMachine.ts @@ -1,10 +1,10 @@ -import { reduce, state, transition } from 'robot3' import { EditCompensationContextual, JobsListContextual, type CompensationFlowContextInterface, type EventPayloads, } from './CompensationFlowComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' @@ -12,7 +12,7 @@ import type { MachineEventType, MachineTransition } from '@/types/Helpers' * `EMPLOYEE_COMPENSATION_DONE` deliberately has no transition out of `initialEditJob` or * `viewJobs`. `Flow` re-emits every event to the upstream `onEvent` regardless of whether the * local machine has a matching transition, so the parent flow still advances/unmounts this - * component on that signal. Modeling completion as a robot3 final state instead left `component` + * component on that signal. Modeling completion as a final state instead left `component` * pointing at a step whose controls could never fire another transition — if a host doesn't * unmount immediately, the screen would look interactive but be permanently dead (SDK-1169). * Staying put means a host that keeps this component mounted past completion keeps a fully diff --git a/src/components/Employee/Dashboard/DashboardFlow.tsx b/src/components/Employee/Dashboard/DashboardFlow.tsx index a0afef8c33..701c750bf6 100644 --- a/src/components/Employee/Dashboard/DashboardFlow.tsx +++ b/src/components/Employee/Dashboard/DashboardFlow.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { dashboardStateMachine } from './dashboardStateMachine' import { type DashboardContextInterface, DashboardViewContextual } from './DashboardComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { BaseComponentInterface } from '@/components/Base' diff --git a/src/components/Employee/Dashboard/dashboardStateMachine.test.ts b/src/components/Employee/Dashboard/dashboardStateMachine.test.ts index 0757a63efa..864a7a375b 100644 --- a/src/components/Employee/Dashboard/dashboardStateMachine.test.ts +++ b/src/components/Employee/Dashboard/dashboardStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { dashboardStateMachine } from './dashboardStateMachine' import { DashboardViewContextual, type DashboardContextInterface } from './DashboardComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Employee/Dashboard/dashboardStateMachine.ts b/src/components/Employee/Dashboard/dashboardStateMachine.ts index eedbef5773..de0c15b4ad 100644 --- a/src/components/Employee/Dashboard/dashboardStateMachine.ts +++ b/src/components/Employee/Dashboard/dashboardStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state } from 'robot3' import type { Garnishment } from '@gusto/embedded-api/models/components/garnishment' import type { DashboardTab } from './Dashboard' import { @@ -17,6 +16,7 @@ import { AddAnotherJobContextual, type DashboardContextInterface, } from './DashboardComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Deductions/management/Deductions.tsx b/src/components/Employee/Deductions/management/Deductions.tsx index 292693c755..319ab4f0b7 100644 --- a/src/components/Employee/Deductions/management/Deductions.tsx +++ b/src/components/Employee/Deductions/management/Deductions.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { DeductionsCardContextual, type DeductionsContextInterface } from './DeductionsComponents' import { deductionsStateMachine } from './deductionsStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/Deductions/management/deductionsStateMachine.ts b/src/components/Employee/Deductions/management/deductionsStateMachine.ts index a1919d05c4..c37561b700 100644 --- a/src/components/Employee/Deductions/management/deductionsStateMachine.ts +++ b/src/components/Employee/Deductions/management/deductionsStateMachine.ts @@ -1,8 +1,8 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { Garnishment } from '@gusto/embedded-api/models/components/garnishment' import type { DeductionsContextInterface } from './DeductionsComponents' import { DeductionsCardContextual, DeductionsEditFormContextual } from './DeductionsComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Deductions/onboarding/Deductions.tsx b/src/components/Employee/Deductions/onboarding/Deductions.tsx index da651d741c..93c85b9abc 100644 --- a/src/components/Employee/Deductions/onboarding/Deductions.tsx +++ b/src/components/Employee/Deductions/onboarding/Deductions.tsx @@ -1,10 +1,10 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { DeductionsListContextual, type DeductionsContextInterface, } from './deductionsContextualComponents' import { deductionsMachine } from './stateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base/Base' import { useComponentDictionary, useI18n } from '@/i18n' diff --git a/src/components/Employee/Deductions/onboarding/stateMachine.test.ts b/src/components/Employee/Deductions/onboarding/stateMachine.test.ts index 85d077cf27..5426f592f4 100644 --- a/src/components/Employee/Deductions/onboarding/stateMachine.test.ts +++ b/src/components/Employee/Deductions/onboarding/stateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { deductionsMachine } from './stateMachine' import type { DeductionsContextInterface } from './deductionsContextualComponents' import { componentEvents } from '@/shared/constants' @@ -23,7 +23,7 @@ function send(service: ReturnType, type: string, payload?: } describe('deductionsMachine', () => { - // Regression test for SDK-1169: `done` used to be a robot3 final state (no + // Regression test for SDK-1169: `done` used to be a final state (no // transitions out) reached from `list` on EMPLOYEE_DEDUCTION_DONE. A host that kept // this component mounted past that signal was left with a deductions list whose // Add/Edit controls could never fire another transition. EMPLOYEE_DEDUCTION_DONE now diff --git a/src/components/Employee/Deductions/onboarding/stateMachine.ts b/src/components/Employee/Deductions/onboarding/stateMachine.ts index 0dfe5244b3..96531079e0 100644 --- a/src/components/Employee/Deductions/onboarding/stateMachine.ts +++ b/src/components/Employee/Deductions/onboarding/stateMachine.ts @@ -1,9 +1,9 @@ -import { state, transition, reduce } from 'robot3' import type { DeductionsContextInterface, EventPayloads } from './deductionsContextualComponents' import { DeductionsListContextual, DeductionsFormContextual, } from './deductionsContextualComponents' +import { state, transition, reduce } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' @@ -11,7 +11,7 @@ import type { MachineEventType, MachineTransition } from '@/types/Helpers' * `EMPLOYEE_DEDUCTION_DONE` deliberately has no transition out of `list`. `Flow` re-emits every * event to the upstream `onEvent` regardless of whether the local machine has a matching * transition, so the parent flow still advances/unmounts this component on that signal. Modeling - * completion as a robot3 final state instead left `component` pointing at a step whose controls + * completion as a final state instead left `component` pointing at a step whose controls * could never fire another transition — if a host doesn't unmount immediately, the screen would * look interactive but be permanently dead (see SDK-1169, the same bug in * `Compensation`'s onboarding machine). Staying in `list` means a host that keeps this component diff --git a/src/components/Employee/Documents/management/Documents.tsx b/src/components/Employee/Documents/management/Documents.tsx index 9f90d261dd..91ec698592 100644 --- a/src/components/Employee/Documents/management/Documents.tsx +++ b/src/components/Employee/Documents/management/Documents.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { DocumentsCardContextual, type DocumentsContextInterface } from './DocumentsComponents' import { documentsStateMachine } from './documentsStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/Documents/management/documentsStateMachine.ts b/src/components/Employee/Documents/management/documentsStateMachine.ts index 72c084e763..f8ec2bec7b 100644 --- a/src/components/Employee/Documents/management/documentsStateMachine.ts +++ b/src/components/Employee/Documents/management/documentsStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { DocumentsContextInterface } from './DocumentsComponents' import { DocumentsCardContextual, DocumentManagerContextual } from './DocumentsComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Documents/onboarding/DocumentSigner/DocumentSigner.tsx b/src/components/Employee/Documents/onboarding/DocumentSigner/DocumentSigner.tsx index 690d4b01c7..f9c79f77d5 100644 --- a/src/components/Employee/Documents/onboarding/DocumentSigner/DocumentSigner.tsx +++ b/src/components/Employee/Documents/onboarding/DocumentSigner/DocumentSigner.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { useEmployeesGetSuspense } from '@gusto/embedded-api/react-query/employeesGet' import { useEmployeeFormsListSuspense } from '@gusto/embedded-api/react-query/employeeFormsList' @@ -8,6 +7,7 @@ import { type DocumentSignerContextInterface, } from './documentSignerStateMachine' import { documentSignerMachine } from './stateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseComponent, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.test.ts b/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.test.ts index 76352805d0..62dc901e58 100644 --- a/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.test.ts +++ b/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { documentSignerMachine } from './stateMachine' import type { DocumentSignerContextInterface } from './documentSignerStateMachine' import { componentEvents } from '@/shared/constants' @@ -27,7 +27,7 @@ function send(service: ReturnType, type: string, payload?: } describe('documentSignerMachine', () => { - // Regression test for SDK-1169: `done` used to be a robot3 final state (no + // Regression test for SDK-1169: `done` used to be a final state (no // transitions out) reached from `index` on EMPLOYEE_FORMS_DONE. A host that kept // this component mounted past that signal was left with a document list whose // "view form to sign" controls could never fire another transition. diff --git a/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.ts b/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.ts index 5bcc842df7..9f48b9055b 100644 --- a/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.ts +++ b/src/components/Employee/Documents/onboarding/DocumentSigner/stateMachine.ts @@ -1,4 +1,3 @@ -import { state, transition, reduce } from 'robot3' import type { DocumentSignerContextInterface, EventPayloads } from './documentSignerStateMachine' import { SignatureFormContextual, @@ -6,6 +5,7 @@ import { I9SignatureFormContextual, EmploymentEligibilityContextual, } from './documentSignerStateMachine' +import { state, transition, reduce } from '@/lib/state-machine' import { componentEvents, I9_FORM_NAME } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' @@ -13,7 +13,7 @@ import type { MachineEventType, MachineTransition } from '@/types/Helpers' * `EMPLOYEE_FORMS_DONE` deliberately has no transition out of `index`. `Flow` re-emits every * event to the upstream `onEvent` regardless of whether the local machine has a matching * transition, so the parent flow still advances/unmounts this component on that signal. Modeling - * completion as a robot3 final state instead left `component` pointing at a step whose controls + * completion as a final state instead left `component` pointing at a step whose controls * could never fire another transition — if a host doesn't unmount immediately, the screen would * look interactive but be permanently dead (see SDK-1169, the same bug in * `Compensation`'s onboarding machine). Staying in `index` means a host that keeps this diff --git a/src/components/Employee/EmployeeListFlow/EmployeeListFlow.tsx b/src/components/Employee/EmployeeListFlow/EmployeeListFlow.tsx index 46c646f390..3c6aefcbe0 100644 --- a/src/components/Employee/EmployeeListFlow/EmployeeListFlow.tsx +++ b/src/components/Employee/EmployeeListFlow/EmployeeListFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { employeeListStateMachine } from './employeeListStateMachine' import { @@ -6,6 +5,7 @@ import { type EmployeeListFlowContextInterface, type EmployeeListFlowProps, } from './EmployeeListFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' /** diff --git a/src/components/Employee/EmployeeListFlow/employeeListStateMachine.ts b/src/components/Employee/EmployeeListFlow/employeeListStateMachine.ts index 4881f966c0..2e9bc53f9d 100644 --- a/src/components/Employee/EmployeeListFlow/employeeListStateMachine.ts +++ b/src/components/Employee/EmployeeListFlow/employeeListStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state } from 'robot3' import { DashboardFlowContextual, EmployeeListContextual, @@ -6,6 +5,7 @@ import { TerminationFlowContextual, type EmployeeListFlowContextInterface, } from './EmployeeListFlowComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import type { FlowHeaderConfig } from '@/components/Flow/useFlow' diff --git a/src/components/Employee/FederalTaxes/management/FederalTaxes.tsx b/src/components/Employee/FederalTaxes/management/FederalTaxes.tsx index 412d605a46..be91f1a288 100644 --- a/src/components/Employee/FederalTaxes/management/FederalTaxes.tsx +++ b/src/components/Employee/FederalTaxes/management/FederalTaxes.tsx @@ -1,10 +1,10 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { FederalTaxesCardContextual, type FederalTaxesContextInterface, } from './FederalTaxesComponents' import { federalTaxesStateMachine } from './federalTaxesStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/FederalTaxes/management/federalTaxesStateMachine.ts b/src/components/Employee/FederalTaxes/management/federalTaxesStateMachine.ts index 44db5a2965..3904660edc 100644 --- a/src/components/Employee/FederalTaxes/management/federalTaxesStateMachine.ts +++ b/src/components/Employee/FederalTaxes/management/federalTaxesStateMachine.ts @@ -1,10 +1,10 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { FederalTaxesContextInterface } from './FederalTaxesComponents' import { FederalTaxesCardContextual, FederalTaxesEditFormContextual, } from './FederalTaxesComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/HomeAddress/management/HomeAddress.tsx b/src/components/Employee/HomeAddress/management/HomeAddress.tsx index 0f2219a0d0..4a99d259b7 100644 --- a/src/components/Employee/HomeAddress/management/HomeAddress.tsx +++ b/src/components/Employee/HomeAddress/management/HomeAddress.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CardContextual, type HomeAddressContextInterface } from './HomeAddressComponents' import { homeAddressStateMachine } from './homeAddressStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/HomeAddress/management/homeAddressStateMachine.ts b/src/components/Employee/HomeAddress/management/homeAddressStateMachine.ts index 3913b72a53..109850c831 100644 --- a/src/components/Employee/HomeAddress/management/homeAddressStateMachine.ts +++ b/src/components/Employee/HomeAddress/management/homeAddressStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { HomeAddressContextInterface } from './HomeAddressComponents' import { CardContextual, HomeAddressEditFormContextual } from './HomeAddressComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/OnboardingExecutionFlow/OnboardingExecutionFlow.tsx b/src/components/Employee/OnboardingExecutionFlow/OnboardingExecutionFlow.tsx index 2320d8ee5c..e7ac45841c 100644 --- a/src/components/Employee/OnboardingExecutionFlow/OnboardingExecutionFlow.tsx +++ b/src/components/Employee/OnboardingExecutionFlow/OnboardingExecutionFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { onboardingExecutionMachine, @@ -9,6 +8,7 @@ import { type OnboardingContextInterface, type OnboardingDefaultValues, } from './OnboardingExecutionFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { OnEventType } from '@/components/Base/useBase' import type { EventType, EmployeeOnboardingStatus } from '@/shared/constants' diff --git a/src/components/Employee/OnboardingExecutionFlow/onboardingExecutionStateMachine.ts b/src/components/Employee/OnboardingExecutionFlow/onboardingExecutionStateMachine.ts index d29571ce21..ea66601807 100644 --- a/src/components/Employee/OnboardingExecutionFlow/onboardingExecutionStateMachine.ts +++ b/src/components/Employee/OnboardingExecutionFlow/onboardingExecutionStateMachine.ts @@ -1,10 +1,10 @@ -import { transition, reduce, state, guard } from 'robot3' import { FederalTaxesContextual, StateTaxesContextual, DeductionsContextual, type OnboardingContextInterface, } from './OnboardingExecutionFlowComponents' +import { transition, reduce, state, guard } from '@/lib/state-machine' import { componentEvents, EmployeeSelfOnboardingStatuses, diff --git a/src/components/Employee/OnboardingFlow/OnboardingFlow.tsx b/src/components/Employee/OnboardingFlow/OnboardingFlow.tsx index ad501f9c91..e8dc27206f 100644 --- a/src/components/Employee/OnboardingFlow/OnboardingFlow.tsx +++ b/src/components/Employee/OnboardingFlow/OnboardingFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { employeeOnboardingMachine } from './onboardingStateMachine' import { @@ -6,6 +5,7 @@ import { type OnboardingContextInterface, EmployeeListContextual, } from './OnboardingFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { BaseComponentInterface } from '@/components/Base' import type { RequireAtLeastOne } from '@/types/Helpers' diff --git a/src/components/Employee/OnboardingFlow/onboardingStateMachine.ts b/src/components/Employee/OnboardingFlow/onboardingStateMachine.ts index 425b517783..42d2ca4a63 100644 --- a/src/components/Employee/OnboardingFlow/onboardingStateMachine.ts +++ b/src/components/Employee/OnboardingFlow/onboardingStateMachine.ts @@ -1,9 +1,9 @@ -import { transition, reduce, state } from 'robot3' import { EmployeeListContextual, OnboardingExecutionFlowContextual, type OnboardingContextInterface, } from './OnboardingFlowComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents, type EmployeeOnboardingStatus } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/PaymentMethod/management/PaymentMethod.tsx b/src/components/Employee/PaymentMethod/management/PaymentMethod.tsx index e66f1b2326..4d4304c117 100644 --- a/src/components/Employee/PaymentMethod/management/PaymentMethod.tsx +++ b/src/components/Employee/PaymentMethod/management/PaymentMethod.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import type { PaymentMethodContextInterface } from './PaymentMethodComponents' import { @@ -7,6 +6,7 @@ import { PaymentMethodSplitFormContextual, } from './PaymentMethodComponents' import { paymentMethodStateMachine } from './paymentMethodStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/PaymentMethod/management/paymentMethodStateMachine.ts b/src/components/Employee/PaymentMethod/management/paymentMethodStateMachine.ts index 9f4eca45cd..be4d983443 100644 --- a/src/components/Employee/PaymentMethod/management/paymentMethodStateMachine.ts +++ b/src/components/Employee/PaymentMethod/management/paymentMethodStateMachine.ts @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { PaymentMethodContextInterface, @@ -9,6 +8,7 @@ import { PaymentMethodBankFormContextual, PaymentMethodSplitFormContextual, } from './PaymentMethodComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/PaymentMethod/onboarding/PaymentMethod.tsx b/src/components/Employee/PaymentMethod/onboarding/PaymentMethod.tsx index 8933b1143e..600fd96618 100644 --- a/src/components/Employee/PaymentMethod/onboarding/PaymentMethod.tsx +++ b/src/components/Employee/PaymentMethod/onboarding/PaymentMethod.tsx @@ -1,9 +1,9 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import type { OnboardingContextInterface } from '../../OnboardingFlow/OnboardingFlowComponents' import type { PaymentMethodContextInterface } from './PaymentMethodComponents' import { ListViewContextual } from './PaymentMethodComponents' import { paymentMethodStateMachine } from './paymentMethodStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { useFlow } from '@/components/Flow/useFlow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' diff --git a/src/components/Employee/PaymentMethod/onboarding/paymentMethodStateMachine.ts b/src/components/Employee/PaymentMethod/onboarding/paymentMethodStateMachine.ts index 54a0e0ad77..726fc335cd 100644 --- a/src/components/Employee/PaymentMethod/onboarding/paymentMethodStateMachine.ts +++ b/src/components/Employee/PaymentMethod/onboarding/paymentMethodStateMachine.ts @@ -1,4 +1,3 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { PaymentMethodContextInterface } from './PaymentMethodComponents' import { @@ -6,6 +5,7 @@ import { BankFormContextual, SplitViewContextual, } from './PaymentMethodComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Profile/management/Profile.tsx b/src/components/Employee/Profile/management/Profile.tsx index 51be21b8cf..f2b7803442 100644 --- a/src/components/Employee/Profile/management/Profile.tsx +++ b/src/components/Employee/Profile/management/Profile.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { CardContextual, type ProfileContextInterface } from './ProfileComponents' import { profileStateMachine } from './profileStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/Profile/management/profileStateMachine.ts b/src/components/Employee/Profile/management/profileStateMachine.ts index d19f909223..5dd4d1e40b 100644 --- a/src/components/Employee/Profile/management/profileStateMachine.ts +++ b/src/components/Employee/Profile/management/profileStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { ProfileContextInterface } from './ProfileComponents' import { CardContextual, ProfileEditFormContextual } from './ProfileComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/SelfOnboardingFlow/SelfOnboardingFlow.tsx b/src/components/Employee/SelfOnboardingFlow/SelfOnboardingFlow.tsx index 12dab43d54..f39cb721c4 100644 --- a/src/components/Employee/SelfOnboardingFlow/SelfOnboardingFlow.tsx +++ b/src/components/Employee/SelfOnboardingFlow/SelfOnboardingFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import type { SelfOnboardingContextInterface, @@ -6,6 +5,7 @@ import type { } from './SelfOnboardingComponents' import { Landing } from './SelfOnboardingComponents' import { employeeSelfOnboardingMachine } from './selfOnboardingMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' export type { SelfOnboardingFlowProps } from './SelfOnboardingComponents' diff --git a/src/components/Employee/SelfOnboardingFlow/selfOnboardingMachine.ts b/src/components/Employee/SelfOnboardingFlow/selfOnboardingMachine.ts index 2d55d75f1e..2179186498 100644 --- a/src/components/Employee/SelfOnboardingFlow/selfOnboardingMachine.ts +++ b/src/components/Employee/SelfOnboardingFlow/selfOnboardingMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state } from 'robot3' import type { SelfOnboardingContextInterface } from './SelfOnboardingComponents' import { Profile, @@ -8,6 +7,7 @@ import { OnboardingSummary, DocumentSigner, } from './SelfOnboardingComponents' +import { transition, reduce, state } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/StateTaxes/management/StateTaxes.tsx b/src/components/Employee/StateTaxes/management/StateTaxes.tsx index 903c527d76..a21ea608d1 100644 --- a/src/components/Employee/StateTaxes/management/StateTaxes.tsx +++ b/src/components/Employee/StateTaxes/management/StateTaxes.tsx @@ -1,7 +1,7 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { StateTaxesCardContextual, type StateTaxesContextInterface } from './StateTaxesComponents' import { stateTaxesStateMachine } from './stateTaxesStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/StateTaxes/management/stateTaxesStateMachine.ts b/src/components/Employee/StateTaxes/management/stateTaxesStateMachine.ts index 7084de0d94..5e51dbe82c 100644 --- a/src/components/Employee/StateTaxes/management/stateTaxesStateMachine.ts +++ b/src/components/Employee/StateTaxes/management/stateTaxesStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { StateTaxesContextInterface, StateTaxesSuccessAlertCode } from './StateTaxesComponents' import { StateTaxesCardContextual, StateTaxesEditFormContextual } from './StateTaxesComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Employee/Terminations/TerminationFlow/TerminationFlow.tsx b/src/components/Employee/Terminations/TerminationFlow/TerminationFlow.tsx index 3f2da76c1c..e69e246c9d 100644 --- a/src/components/Employee/Terminations/TerminationFlow/TerminationFlow.tsx +++ b/src/components/Employee/Terminations/TerminationFlow/TerminationFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { terminationMachine, terminationBreadcrumbNodes } from './terminationStateMachine' import type { @@ -6,6 +5,7 @@ import type { TerminationFlowContextInterface, } from './TerminationFlowComponents' import { TerminateEmployeeContextual } from './TerminationFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.test.ts b/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.test.ts index 0e4e5318f1..c46693777b 100644 --- a/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.test.ts +++ b/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { terminationMachine, terminationBreadcrumbNodes } from './terminationStateMachine' import type { TerminationFlowContextInterface } from './TerminationFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.ts b/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.ts index 6bb55d8203..a228632f8b 100644 --- a/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.ts +++ b/src/components/Employee/Terminations/TerminationFlow/terminationStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state, guard } from 'robot3' import type { PayrollOption } from '../types' import type { TerminationFlowContextInterface, @@ -10,6 +9,7 @@ import { DismissalFlowContextual, PayrollLandingContextual, } from './TerminationFlowComponents' +import { transition, reduce, state, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import type { BreadcrumbNodes } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' diff --git a/src/components/Employee/WorkAddress/management/WorkAddress.tsx b/src/components/Employee/WorkAddress/management/WorkAddress.tsx index 572b73f55a..659b3e78d8 100644 --- a/src/components/Employee/WorkAddress/management/WorkAddress.tsx +++ b/src/components/Employee/WorkAddress/management/WorkAddress.tsx @@ -1,10 +1,10 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { WorkAddressCardContextual, type WorkAddressContextInterface, } from './WorkAddressComponents' import { workAddressStateMachine } from './workAddressStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { useComponentDictionary } from '@/i18n/I18n' diff --git a/src/components/Employee/WorkAddress/management/workAddressStateMachine.ts b/src/components/Employee/WorkAddress/management/workAddressStateMachine.ts index b0c28309db..bd4d4f6df1 100644 --- a/src/components/Employee/WorkAddress/management/workAddressStateMachine.ts +++ b/src/components/Employee/WorkAddress/management/workAddressStateMachine.ts @@ -1,7 +1,7 @@ -import { reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import type { WorkAddressContextInterface } from './WorkAddressComponents' import { WorkAddressCardContextual, WorkAddressEditFormContextual } from './WorkAddressComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Flow/Flow.test.tsx b/src/components/Flow/Flow.test.tsx index dddd311072..d333c7e14c 100644 --- a/src/components/Flow/Flow.test.tsx +++ b/src/components/Flow/Flow.test.tsx @@ -1,11 +1,11 @@ import { describe, expect, it, vi } from 'vitest' import { screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { createMachine, reduce, state, transition } from 'robot3' import type { ComponentType } from 'react' import { Flow } from './Flow' import type { FlowContextInterface } from './useFlow' import { useFlow } from './useFlow' +import { createMachine, reduce, state, transition } from '@/lib/state-machine' import { componentEvents, type EventType } from '@/shared/constants' import { renderWithProviders } from '@/test-utils/renderWithProviders' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/components/Flow/Flow.tsx b/src/components/Flow/Flow.tsx index c99f43823d..bfd8784574 100644 --- a/src/components/Flow/Flow.tsx +++ b/src/components/Flow/Flow.tsx @@ -1,11 +1,10 @@ import { Suspense } from 'react' -import { useMachine } from 'react-robot' -import { type Machine, type SendFunction } from 'robot3' import type { OnEventType } from '../Base/useBase' import { Flex } from '../Common/Flex' import type { FlowContextInterface } from './useFlow' import { FlowContext } from './useFlow' import { FlowHeader } from './FlowHeader' +import { useMachine, type Machine } from '@/lib/state-machine' import { useLoadingIndicator } from '@/contexts/LoadingIndicatorProvider/useLoadingIndicator' import { type EventType } from '@/shared/constants' @@ -35,16 +34,15 @@ type FlowProps = { } /** - * Drives a robot3 state machine that orchestrates a multi-step SDK flow, rendering the active + * Drives a state machine that orchestrates a multi-step SDK flow, rendering the active * step component inside a {@link FlowContext} provider with optional header chrome above it. * * @remarks - * Re-emits every event produced by the state machine (or the active child machine, when one is - * nested) to the upstream `onEvent` callback, so the consuming flow component (e.g. `PayrollFlow`, - * `OnboardingFlow`) can forward them to partner code. The set of event types is defined by the - * machine itself rather than a fixed catalogue. + * Re-emits every event to the upstream `onEvent` callback, so the consuming flow component + * (e.g. `PayrollFlow`, `OnboardingFlow`) can forward them to partner code. The set of event + * types is defined by the machine itself rather than a fixed catalogue. * - * @typeParam M - The robot3 {@link Machine} type whose context extends {@link FlowContextInterface}. + * @typeParam M - The {@link Machine} type whose context extends {@link FlowContextInterface}. * @param props - Component props: the `machine` instance to run and an `onEvent` handler that * receives every event the machine emits. * @returns A React element rendering the machine's current `component` inside the flow context, @@ -55,21 +53,13 @@ export const Flow = >({ onEvent, machine, }: FlowProps) => { - const [current, send, service] = useMachine(machine, { + const [current, send] = useMachine(machine, { onEvent: handleEvent, component: null, }) function handleEvent(type: EventType, data: unknown): void { - const event = { type, payload: data } - const sendFn = send as SendFunction - //When dealing with nested state machine, correct machine needs to recieve an event - if (service.child) { - ;(service.child.send as SendFunction)(event) - } else { - sendFn(event) - } - // Pass event upstream - onEvent can be optional on Flow component + send({ type, payload: data }) onEvent(type, data) } diff --git a/src/components/InformationRequests/InformationRequests.tsx b/src/components/InformationRequests/InformationRequests.tsx index 852d95b6fd..35d620f9d1 100644 --- a/src/components/InformationRequests/InformationRequests.tsx +++ b/src/components/InformationRequests/InformationRequests.tsx @@ -1,10 +1,9 @@ -import { createMachine } from 'robot3' -import { useMachine } from 'react-robot' import { Suspense, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { InformationRequestList } from './InformationRequestList' import { informationRequestsMachine } from './informationRequestsStateMachine' import { type InformationRequestsContextInterface } from './InformationRequestsComponents' +import { createMachine, useMachine } from '@/lib/state-machine' import { BaseBoundaries, type BaseComponentInterface } from '@/components/Base' import { Flex } from '@/components/Common' import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext' diff --git a/src/components/InformationRequests/informationRequestsStateMachine.tsx b/src/components/InformationRequests/informationRequestsStateMachine.tsx index b4d41b286f..b17aa32491 100644 --- a/src/components/InformationRequests/informationRequestsStateMachine.tsx +++ b/src/components/InformationRequests/informationRequestsStateMachine.tsx @@ -1,6 +1,6 @@ -import { state, transition, reduce } from 'robot3' import type { InformationRequestsContextInterface } from './InformationRequestsComponents' import { InformationRequestFormContextual } from './InformationRequestsComponents' +import { state, transition, reduce } from '@/lib/state-machine' import { informationRequestEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Payroll/ConfirmWireDetails/ConfirmWireDetails.tsx b/src/components/Payroll/ConfirmWireDetails/ConfirmWireDetails.tsx index 36dcfb1c81..79e371ea8f 100644 --- a/src/components/Payroll/ConfirmWireDetails/ConfirmWireDetails.tsx +++ b/src/components/Payroll/ConfirmWireDetails/ConfirmWireDetails.tsx @@ -1,5 +1,3 @@ -import { createMachine } from 'robot3' -import { useMachine } from 'react-robot' import { Suspense, useMemo, useRef, useState } from 'react' import { useWireInRequestsListSuspense } from '@gusto/embedded-api/react-query/wireInRequestsList' import { ConfirmWireDetailsBanner } from './ConfirmWireDetailsBanner' @@ -11,6 +9,7 @@ import styles from './ConfirmWireDetails.module.scss' import { BaseComponent, BaseBoundaries } from '@/components/Base' import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext' import { FlowContext } from '@/components/Flow/useFlow' +import { createMachine, useMachine } from '@/lib/state-machine' import { payrollWireEvents, type EventType } from '@/shared/constants' /** diff --git a/src/components/Payroll/ConfirmWireDetails/confirmWireDetailsStateMachine.tsx b/src/components/Payroll/ConfirmWireDetails/confirmWireDetailsStateMachine.tsx index e6fd3cd790..9e3bfb89e2 100644 --- a/src/components/Payroll/ConfirmWireDetails/confirmWireDetailsStateMachine.tsx +++ b/src/components/Payroll/ConfirmWireDetails/confirmWireDetailsStateMachine.tsx @@ -1,10 +1,10 @@ -import { state, transition, reduce } from 'robot3' import type { ConfirmationAlert } from './types' import type { ConfirmWireDetailsContextInterface } from './ConfirmWireDetailsComponents' import { WireInstructionsContextual, ConfirmWireDetailsFormContextual, } from './ConfirmWireDetailsComponents' +import { state, transition, reduce } from '@/lib/state-machine' import { payrollWireEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Payroll/Dismissal/DismissalFlow.tsx b/src/components/Payroll/Dismissal/DismissalFlow.tsx index 33eef4effd..b1408fccaa 100644 --- a/src/components/Payroll/Dismissal/DismissalFlow.tsx +++ b/src/components/Payroll/Dismissal/DismissalFlow.tsx @@ -1,5 +1,4 @@ import { useMemo } from 'react' -import { createMachine } from 'robot3' import { dismissalMachine, dismissalBreadcrumbsNodes } from './dismissalStateMachine' import { DismissalPayPeriodSelectionContextual, @@ -7,6 +6,7 @@ import { type DismissalFlowContextInterface, type DismissalFlowProps, } from './DismissalFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { BaseComponent } from '@/components/Base/Base' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/Dismissal/dismissalStateMachine.test.ts b/src/components/Payroll/Dismissal/dismissalStateMachine.test.ts index 8e49a6c9ef..7b8cf7197b 100644 --- a/src/components/Payroll/Dismissal/dismissalStateMachine.test.ts +++ b/src/components/Payroll/Dismissal/dismissalStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { dismissalMachine, dismissalBreadcrumbsNodes } from './dismissalStateMachine' import type { DismissalFlowContextInterface } from './DismissalFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Payroll/Dismissal/dismissalStateMachine.ts b/src/components/Payroll/Dismissal/dismissalStateMachine.ts index 1f601d9536..73f1c6c013 100644 --- a/src/components/Payroll/Dismissal/dismissalStateMachine.ts +++ b/src/components/Payroll/Dismissal/dismissalStateMachine.ts @@ -1,9 +1,9 @@ -import { state, transition, reduce, guard } from 'robot3' import { DismissalPayPeriodSelectionContextual, DismissalExecutionContextual, type DismissalFlowContextInterface, } from './DismissalFlowComponents' +import { state, transition, reduce, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' import type { BreadcrumbNodes } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' diff --git a/src/components/Payroll/OffCycle/OffCycleExecution.test.tsx b/src/components/Payroll/OffCycle/OffCycleExecution.test.tsx index 5737635553..8eb4192a84 100644 --- a/src/components/Payroll/OffCycle/OffCycleExecution.test.tsx +++ b/src/components/Payroll/OffCycle/OffCycleExecution.test.tsx @@ -3,10 +3,10 @@ import { screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { http, HttpResponse } from 'msw' import { useMemo } from 'react' -import { createMachine } from 'robot3' import type { OffCycleFlowContextInterface } from './OffCycleFlowComponents' import { OffCycleExecutionContextual } from './OffCycleFlowComponents' import { offCycleMachine, offCycleBreadcrumbsNodes } from './offCycleStateMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' import { server } from '@/test/mocks/server' diff --git a/src/components/Payroll/OffCycle/OffCycleFlow.tsx b/src/components/Payroll/OffCycle/OffCycleFlow.tsx index 7c3991633b..c5206251d0 100644 --- a/src/components/Payroll/OffCycle/OffCycleFlow.tsx +++ b/src/components/Payroll/OffCycle/OffCycleFlow.tsx @@ -1,11 +1,11 @@ import { useMemo } from 'react' -import { createMachine } from 'robot3' import { offCycleMachine, offCycleBreadcrumbsNodes } from './offCycleStateMachine' import { OffCycleCreationContextual, type OffCycleFlowContextInterface, type OffCycleFlowProps, } from './OffCycleFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/OffCycle/offCycleStateMachine.test.ts b/src/components/Payroll/OffCycle/offCycleStateMachine.test.ts index 2cb8fc0221..9c6aa46142 100644 --- a/src/components/Payroll/OffCycle/offCycleStateMachine.test.ts +++ b/src/components/Payroll/OffCycle/offCycleStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { offCycleMachine, offCycleBreadcrumbsNodes } from './offCycleStateMachine' import type { OffCycleFlowContextInterface } from './OffCycleFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Payroll/OffCycle/offCycleStateMachine.ts b/src/components/Payroll/OffCycle/offCycleStateMachine.ts index 4c20fc52a3..6e76a901b7 100644 --- a/src/components/Payroll/OffCycle/offCycleStateMachine.ts +++ b/src/components/Payroll/OffCycle/offCycleStateMachine.ts @@ -1,9 +1,9 @@ -import { state, transition, reduce, guard } from 'robot3' import { OffCycleCreationContextual, OffCycleExecutionContextual, type OffCycleFlowContextInterface, } from './OffCycleFlowComponents' +import { state, transition, reduce, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' import type { BreadcrumbNodes } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' diff --git a/src/components/Payroll/PayrollExecutionFlow/PayrollExecutionFlow.tsx b/src/components/Payroll/PayrollExecutionFlow/PayrollExecutionFlow.tsx index ee1dc59014..5784ba3a42 100644 --- a/src/components/Payroll/PayrollExecutionFlow/PayrollExecutionFlow.tsx +++ b/src/components/Payroll/PayrollExecutionFlow/PayrollExecutionFlow.tsx @@ -1,5 +1,4 @@ import { useMemo } from 'react' -import { createMachine } from 'robot3' import type { PayrollPayPeriodType } from '@gusto/embedded-api/models/components/payrollpayperiodtype' import type { ConfirmWireDetailsComponentType } from '../ConfirmWireDetails/ConfirmWireDetails' import { @@ -12,6 +11,7 @@ import { payrollExecutionMachine, getPayrollExecutionBreadcrumbsNodes, } from './payrollExecutionMachine' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { FlowBreadcrumb } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' import { buildBreadcrumbs, updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.test.ts b/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.test.ts index 3e0fffd73f..4ebb4be9e5 100644 --- a/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.test.ts +++ b/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { payrollExecutionMachine, getPayrollExecutionBreadcrumbsNodes, diff --git a/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.ts b/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.ts index b064e197a0..78abb03ee2 100644 --- a/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.ts +++ b/src/components/Payroll/PayrollExecutionFlow/payrollExecutionMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state, guard } from 'robot3' import type { PayrollPayPeriodType } from '@gusto/embedded-api/models/components/payrollpayperiodtype' import type { PayrollFlowAlert } from '../PayrollFlow/PayrollFlowComponents' import { @@ -9,6 +8,7 @@ import { PayrollBlockerContextual, type PayrollFlowContextInterface, } from '../PayrollFlow/PayrollFlowComponents' +import { transition, reduce, state, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { diff --git a/src/components/Payroll/PayrollFlow/PayrollFlow.tsx b/src/components/Payroll/PayrollFlow/PayrollFlow.tsx index 9fc9bddb2d..84267acaff 100644 --- a/src/components/Payroll/PayrollFlow/PayrollFlow.tsx +++ b/src/components/Payroll/PayrollFlow/PayrollFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { payrollFlowBreadcrumbsNodes, payrollFlowMachine } from './payrollStateMachine' import type { PayrollFlowProps } from './PayrollFlowComponents' @@ -7,6 +6,7 @@ import { PayrollLandingContextual, type PayrollFlowContextInterface, } from './PayrollFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/PayrollFlow/payrollStateMachine.test.ts b/src/components/Payroll/PayrollFlow/payrollStateMachine.test.ts index 4b50efbf28..be26ea1b3f 100644 --- a/src/components/Payroll/PayrollFlow/payrollStateMachine.test.ts +++ b/src/components/Payroll/PayrollFlow/payrollStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { payrollFlowMachine, payrollFlowBreadcrumbsNodes } from './payrollStateMachine' import type { PayrollFlowContextInterface } from './PayrollFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Payroll/PayrollFlow/payrollStateMachine.ts b/src/components/Payroll/PayrollFlow/payrollStateMachine.ts index a076311a24..32dcd92deb 100644 --- a/src/components/Payroll/PayrollFlow/payrollStateMachine.ts +++ b/src/components/Payroll/PayrollFlow/payrollStateMachine.ts @@ -1,4 +1,3 @@ -import { state, transition, reduce, guard } from 'robot3' import type { PayrollPayPeriodType } from '@gusto/embedded-api/models/components/payrollpayperiodtype' import type { PayrollExecutionInitialState } from '../PayrollExecutionFlow' import { @@ -11,6 +10,7 @@ import { import { PayrollExecutionFlowContextual } from './PayrollExecutionFlowContextual' import { TransitionFlowContextual } from './TransitionFlowContextual' import { OffCycleFlowContextual } from './OffCycleFlowContextual' +import { state, transition, reduce, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { patchBreadcrumbsHeader, updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/PayrollLanding/PayrollLanding.tsx b/src/components/Payroll/PayrollLanding/PayrollLanding.tsx index 6e2164e554..2d6db1a303 100644 --- a/src/components/Payroll/PayrollLanding/PayrollLanding.tsx +++ b/src/components/Payroll/PayrollLanding/PayrollLanding.tsx @@ -1,5 +1,4 @@ import { useMemo } from 'react' -import { createMachine } from 'robot3' import type { ConfirmWireDetailsComponentType } from '../ConfirmWireDetails/ConfirmWireDetails' import { payrollLandingMachine, payrollLandingBreadcrumbNodes } from './payrollLandingStateMachine' import { @@ -7,6 +6,7 @@ import { type PayrollLandingFlowContextInterface, type PayrollLandingFlowProps, } from './PayrollLandingFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { BaseComponentInterface } from '@/components/Base/Base' import { BaseComponent } from '@/components/Base/Base' diff --git a/src/components/Payroll/PayrollLanding/payrollLandingStateMachine.ts b/src/components/Payroll/PayrollLanding/payrollLandingStateMachine.ts index bc73d2c38f..f1ee4d51ea 100644 --- a/src/components/Payroll/PayrollLanding/payrollLandingStateMachine.ts +++ b/src/components/Payroll/PayrollLanding/payrollLandingStateMachine.ts @@ -1,10 +1,10 @@ -import { transition, reduce, state, guard } from 'robot3' import { PayrollLandingTabsContextual, PayrollLandingReceiptsContextual, PayrollLandingOverviewContextual, type PayrollLandingFlowContextInterface, } from './PayrollLandingFlowComponents' +import { transition, reduce, state, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import type { BreadcrumbNodes } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' diff --git a/src/components/Payroll/RecoveryCases/RecoveryCases.tsx b/src/components/Payroll/RecoveryCases/RecoveryCases.tsx index ba30a13ed4..c1e36cf2ab 100644 --- a/src/components/Payroll/RecoveryCases/RecoveryCases.tsx +++ b/src/components/Payroll/RecoveryCases/RecoveryCases.tsx @@ -1,9 +1,8 @@ -import { createMachine } from 'robot3' -import { useMachine } from 'react-robot' import { useMemo, useState } from 'react' import { RecoveryCasesList } from './RecoveryCasesList' import { recoveryCasesMachine } from './recoveryCasesStateMachine' import { type RecoveryCasesContextInterface } from './RecoveryCasesComponents' +import { createMachine, useMachine } from '@/lib/state-machine' import { BaseComponent, type BaseComponentInterface } from '@/components/Base' import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext' import { FlowContext } from '@/components/Flow/useFlow' diff --git a/src/components/Payroll/RecoveryCases/recoveryCasesStateMachine.tsx b/src/components/Payroll/RecoveryCases/recoveryCasesStateMachine.tsx index 832a32c9de..b4e946ca94 100644 --- a/src/components/Payroll/RecoveryCases/recoveryCasesStateMachine.tsx +++ b/src/components/Payroll/RecoveryCases/recoveryCasesStateMachine.tsx @@ -1,6 +1,6 @@ -import { state, transition, reduce } from 'robot3' import type { RecoveryCasesContextInterface } from './RecoveryCasesComponents' import { RecoveryCasesResubmitContextual } from './RecoveryCasesComponents' +import { state, transition, reduce } from '@/lib/state-machine' import { recoveryCasesEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/components/Payroll/Transition/TransitionFlow.tsx b/src/components/Payroll/Transition/TransitionFlow.tsx index 7fb0aa6d15..caa9ca3ba1 100644 --- a/src/components/Payroll/Transition/TransitionFlow.tsx +++ b/src/components/Payroll/Transition/TransitionFlow.tsx @@ -1,5 +1,4 @@ import { useMemo } from 'react' -import { createMachine } from 'robot3' import { transitionMachine, transitionBreadcrumbsNodes } from './transitionStateMachine' import { TransitionCreationContextual, @@ -7,6 +6,7 @@ import { type TransitionFlowContextInterface, type TransitionFlowProps, } from './TransitionFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import { buildBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/Transition/transitionStateMachine.test.ts b/src/components/Payroll/Transition/transitionStateMachine.test.ts index eba09e7fba..85f5791767 100644 --- a/src/components/Payroll/Transition/transitionStateMachine.test.ts +++ b/src/components/Payroll/Transition/transitionStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { transitionMachine, transitionBreadcrumbsNodes } from './transitionStateMachine' import type { TransitionFlowContextInterface } from './TransitionFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/Payroll/Transition/transitionStateMachine.ts b/src/components/Payroll/Transition/transitionStateMachine.ts index 15413f058d..64bd46d4ae 100644 --- a/src/components/Payroll/Transition/transitionStateMachine.ts +++ b/src/components/Payroll/Transition/transitionStateMachine.ts @@ -1,9 +1,9 @@ -import { state, transition, reduce, guard } from 'robot3' import { TransitionCreationContextual, TransitionExecutionContextual, type TransitionFlowContextInterface, } from './TransitionFlowComponents' +import { state, transition, reduce, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' import type { BreadcrumbNodes } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' diff --git a/src/components/TimeOff/TimeOffFlow/TimeOffFlow.tsx b/src/components/TimeOff/TimeOffFlow/TimeOffFlow.tsx index a64c72960b..7d15f3d7e9 100644 --- a/src/components/TimeOff/TimeOffFlow/TimeOffFlow.tsx +++ b/src/components/TimeOff/TimeOffFlow/TimeOffFlow.tsx @@ -1,8 +1,8 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { timeOffMachine } from './timeOffStateMachine' import type { TimeOffFlowProps, TimeOffFlowContextInterface } from './TimeOffFlowComponents' import { PolicyListContextual } from './TimeOffFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' /** diff --git a/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.test.ts b/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.test.ts index 94b962b480..f342a427c8 100644 --- a/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.test.ts +++ b/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createMachine, interpret, type SendFunction } from 'robot3' +import { createMachine, interpret, type SendFunction } from '@/lib/state-machine' import { timeOffMachine } from './timeOffStateMachine' import type { TimeOffFlowContextInterface } from './TimeOffFlowComponents' import { componentEvents } from '@/shared/constants' diff --git a/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.ts b/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.ts index 137e5c16ee..db71e62a11 100644 --- a/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.ts +++ b/src/components/TimeOff/TimeOffFlow/timeOffStateMachine.ts @@ -1,4 +1,3 @@ -import { transition, reduce, state, guard } from 'robot3' import { PolicyListContextual, SelectPolicyTypeContextual, @@ -17,6 +16,7 @@ import { type AddEmployeesSource, } from './TimeOffFlowComponents' import type { TimeOffPolicyType } from './timeOffPolicyTypes' +import { transition, reduce, state, guard } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineTransition } from '@/types/Helpers' diff --git a/src/lib/state-machine/__tests__/core.test.ts b/src/lib/state-machine/__tests__/core.test.ts new file mode 100644 index 0000000000..fa3e0e9ad8 --- /dev/null +++ b/src/lib/state-machine/__tests__/core.test.ts @@ -0,0 +1,246 @@ +import { describe, it, expect, vi } from 'vitest' +import { + guard, + reduce, + transition, + state, + createMachine, + interpret, + applyTransition, +} from '../core' + +describe('state machine core', () => { + describe('applyTransition', () => { + it('transitions to a new state and applies the reducer', () => { + const states = { + a: state( + transition( + 'GO', + 'b', + reduce((ctx: { count: number }) => ({ ...ctx, count: ctx.count + 1 })), + ), + ), + b: state(), + } + + const result = applyTransition(states, 'a', { count: 0 }, { type: 'GO' }) + + expect(result).toMatchObject({ current: 'b', context: { count: 1 } }) + }) + + it('returns null for unhandled events', () => { + const states = { + a: state(transition('GO', 'b')), + } + + const result = applyTransition(states, 'a', {}, { type: 'UNKNOWN' }) + + expect(result).toBeNull() + }) + + it('evaluates guards and picks the first matching transition', () => { + const states = { + a: state( + transition( + 'GO', + 'b', + guard((ctx: { val: string }) => ctx.val === 'yes'), + ), + transition('GO', 'c'), + ), + } + + expect(applyTransition(states, 'a', { val: 'no' }, { type: 'GO' })).toMatchObject({ + current: 'c', + }) + expect(applyTransition(states, 'a', { val: 'yes' }, { type: 'GO' })).toMatchObject({ + current: 'b', + }) + }) + + it('composes multiple guards with AND', () => { + const states = { + a: state( + transition( + 'GO', + 'b', + guard((_ctx: { x: number }) => true), + guard((ctx: { x: number }) => ctx.x > 5), + ), + ), + } + + expect(applyTransition(states, 'a', { x: 3 }, { type: 'GO' })).toBeNull() + expect(applyTransition(states, 'a', { x: 10 }, { type: 'GO' })).toMatchObject({ + current: 'b', + }) + }) + + it('pipes reducers in order', () => { + const states = { + a: state( + transition( + 'GO', + 'b', + reduce((ctx: { items: string[] }) => ({ ...ctx, items: [...ctx.items, 'first'] })), + reduce((ctx: { items: string[] }) => ({ ...ctx, items: [...ctx.items, 'second'] })), + ), + ), + b: state(), + } + + const result = applyTransition(states, 'a', { items: [] }, { type: 'GO' }) + + expect(result).toMatchObject({ + current: 'b', + context: { items: ['first', 'second'] }, + }) + }) + + it('passes the event to reducers', () => { + const states = { + a: state( + transition( + 'SET', + 'b', + reduce((_ctx: { value: string }, ev: { payload: string }) => ({ + value: ev.payload, + })), + ), + ), + b: state(), + } + + const result = applyTransition(states, 'a', { value: '' }, { type: 'SET', payload: 'hello' }) + + expect(result).toMatchObject({ + current: 'b', + context: { value: 'hello' }, + }) + }) + + it('passes the event to guards', () => { + const states = { + a: state( + transition( + 'GO', + 'b', + guard((_ctx: object, ev: { payload: { allowed: boolean } }) => ev.payload.allowed), + ), + ), + } + + expect( + applyTransition(states, 'a', {}, { type: 'GO', payload: { allowed: false } }), + ).toBeNull() + expect( + applyTransition(states, 'a', {}, { type: 'GO', payload: { allowed: true } }), + ).toMatchObject({ current: 'b' }) + }) + + it('warns in dev mode for unhandled events', () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const states = { a: state() } + + applyTransition(states, 'a', {}, { type: 'NOPE' }) + + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('No transition for event "NOPE" in state "a"'), + ) + warnSpy.mockRestore() + }) + }) + + describe('createMachine', () => { + it('stores the initial state, states, and context factory', () => { + const factory = () => ({ value: 42 }) + const machine = createMachine('start', { start: state() }, factory) + + expect(machine.initial).toBe('start') + expect(machine.states.start).toBeDefined() + expect(machine.contextFactory()).toMatchObject({ value: 42 }) + }) + }) + + describe('interpret', () => { + it('creates a service with the initial state and context', () => { + const machine = createMachine('idle', { idle: state(), active: state() }, () => ({ + count: 0, + })) + + const service = interpret(machine, () => {}) + + expect(service.machine.current).toBe('idle') + expect(service.context).toMatchObject({ count: 0 }) + expect(service.child).toBeUndefined() + }) + + it('transitions on send and calls onChange', () => { + const machine = createMachine( + 'a', + { + a: state( + transition( + 'NEXT', + 'b', + reduce((ctx: { step: number }) => ({ step: ctx.step + 1 })), + ), + ), + b: state(), + }, + () => ({ step: 0 }), + ) + + const onChange = vi.fn() + const service = interpret(machine, onChange) + + service.send({ type: 'NEXT' }) + + expect(service.machine.current).toBe('b') + expect(service.context).toMatchObject({ step: 1 }) + expect(onChange).toHaveBeenCalledTimes(1) + }) + + it('does not call onChange when no transition matches', () => { + const machine = createMachine('a', { a: state() }, () => ({})) + + const onChange = vi.fn() + const service = interpret(machine, onChange) + + service.send({ type: 'UNKNOWN' }) + + expect(service.machine.current).toBe('a') + expect(onChange).not.toHaveBeenCalled() + }) + + it('passes initialContext through the context factory', () => { + const machine = createMachine('start', { start: state() }, (init: { id?: string } = {}) => ({ + id: init.id ?? 'default', + })) + + const service = interpret(machine, () => {}, { id: 'custom' }) + + expect(service.context).toMatchObject({ id: 'custom' }) + }) + + it('supports sequential transitions', () => { + const machine = createMachine( + 'a', + { + a: state(transition('NEXT', 'b')), + b: state(transition('NEXT', 'c')), + c: state(), + }, + () => ({}), + ) + + const service = interpret(machine, () => {}) + + service.send({ type: 'NEXT' }) + expect(service.machine.current).toBe('b') + + service.send({ type: 'NEXT' }) + expect(service.machine.current).toBe('c') + }) + }) +}) diff --git a/src/lib/state-machine/__tests__/useMachine.test.tsx b/src/lib/state-machine/__tests__/useMachine.test.tsx new file mode 100644 index 0000000000..f0d2064e95 --- /dev/null +++ b/src/lib/state-machine/__tests__/useMachine.test.tsx @@ -0,0 +1,161 @@ +import { describe, it, expect, vi } from 'vitest' +import { render, screen, act } from '@testing-library/react' +import { createMachine, useMachine, state, transition, reduce } from '..' + +type TestContext = { component: React.ComponentType | null; count: number } + +const First = () =>
First
+const Second = () =>
Second
+ +function buildMachine(initial = 'a') { + return createMachine( + initial, + { + a: state( + transition( + 'NEXT', + 'b', + reduce((ctx: TestContext): TestContext => ({ + ...ctx, + component: Second, + count: ctx.count + 1, + })), + ), + ), + b: state( + transition( + 'BACK', + 'a', + reduce((ctx: TestContext): TestContext => ({ + ...ctx, + component: First, + count: ctx.count + 1, + })), + ), + ), + }, + (): TestContext => ({ component: First, count: 0 }), + ) +} + +function TestHarness({ machine }: { machine: ReturnType }) { + const [current, send] = useMachine(machine) + const Component = current.context.component + return ( +
+
{current.name}
+
{current.context.count}
+ {Component && } + + +
+ ) +} + +describe('useMachine', () => { + it('renders the initial component from the machine context', () => { + render() + + expect(screen.getByTestId('first')).toBeInTheDocument() + expect(screen.getByTestId('state')).toHaveTextContent('a') + expect(screen.getByTestId('count')).toHaveTextContent('0') + }) + + it('transitions on dispatch and re-renders with the new component', () => { + render() + + act(() => { + screen.getByRole('button', { name: 'next' }).click() + }) + + expect(screen.getByTestId('second')).toBeInTheDocument() + expect(screen.getByTestId('state')).toHaveTextContent('b') + expect(screen.getByTestId('count')).toHaveTextContent('1') + }) + + it('supports round-trip transitions', () => { + render() + + act(() => { + screen.getByRole('button', { name: 'next' }).click() + }) + expect(screen.getByTestId('second')).toBeInTheDocument() + + act(() => { + screen.getByRole('button', { name: 'back' }).click() + }) + expect(screen.getByTestId('first')).toBeInTheDocument() + expect(screen.getByTestId('count')).toHaveTextContent('2') + }) + + it('provides a service object with machine.current and context', () => { + let lastService: ReturnType>[2] | undefined + + function ServiceInspector({ machine }: { machine: ReturnType }) { + const [, , service] = useMachine(machine) + lastService = service + return null + } + + render() + + expect(lastService?.machine.current).toBe('a') + expect(lastService?.context.count).toBe(0) + expect(lastService?.child).toBeUndefined() + }) + + it('re-initializes when the machine reference changes', () => { + const machine1 = buildMachine('a') + const machine2 = buildMachine('b') + + const { rerender } = render() + expect(screen.getByTestId('state')).toHaveTextContent('a') + + rerender() + expect(screen.getByTestId('state')).toHaveTextContent('b') + }) + + it('passes initialContext through the context factory', () => { + type Ctx = { label: string } + const machine = createMachine('start', { start: state() }, (init?: Partial): Ctx => ({ + label: init?.label ?? 'default', + })) + + function Inspector() { + const [current] = useMachine(machine, { label: 'custom' }) + return
{current.context.label}
+ } + + render() + expect(screen.getByTestId('label')).toHaveTextContent('custom') + }) + + it('ignores events with no matching transition', () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + render() + + act(() => { + screen.getByRole('button', { name: 'back' }).click() + }) + + expect(screen.getByTestId('state')).toHaveTextContent('a') + expect(screen.getByTestId('count')).toHaveTextContent('0') + + warnSpy.mockRestore() + }) +}) diff --git a/src/lib/state-machine/core.ts b/src/lib/state-machine/core.ts new file mode 100644 index 0000000000..a8c2669de0 --- /dev/null +++ b/src/lib/state-machine/core.ts @@ -0,0 +1,140 @@ +/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-parameters */ +import type { + Guard, + Reducer, + TransitionDefinition, + StateDefinition, + Machine, + ContextFunction, + SendEvent, + Service, +} from './types' + +/** @internal */ +export function guard(fn: (ctx: C, ev: any) => boolean): Guard { + return { _tag: 'guard', fn } +} + +/** @internal */ +export function reduce(fn: (ctx: C, ev: any) => C): Reducer { + return { _tag: 'reduce', fn } +} + +/** @internal */ +export function transition( + event: string, + target: string, + ...modifiers: (Guard | Reducer)[] +): TransitionDefinition { + const guards: Guard[] = [] + const reducers: Reducer[] = [] + for (const mod of modifiers) { + if (mod._tag === 'guard') guards.push(mod) + else reducers.push(mod) + } + return { _tag: 'transition', event, target, guards, reducers } +} + +/** @internal */ +export function state<_T = unknown>(...transitions: TransitionDefinition[]): StateDefinition { + const transitionMap = new Map() + for (const t of transitions) { + const existing = transitionMap.get(t.event) + if (existing) { + existing.push(t) + } else { + transitionMap.set(t.event, [t]) + } + } + return { _tag: 'state', transitions: transitionMap } +} + +/** @internal */ +export function createMachine( + initial: string, + states: Record, + contextFactory: ContextFunction, +): Machine { + return { initial, states, contextFactory } +} + +/** @internal */ +export type ApplyResult = { current: string; context: C } + +/** @internal */ +export function applyTransition( + states: Record, + currentState: string, + context: C, + event: SendEvent, +): ApplyResult | null { + const stateDef = states[currentState] + if (!stateDef) { + if (import.meta.env.DEV) { + // eslint-disable-next-line no-console + console.warn(`[state-machine] Unknown state "${currentState}"`) + } + return null + } + + const candidates = stateDef.transitions.get(event.type) + if (!candidates || candidates.length === 0) { + if (import.meta.env.DEV) { + // eslint-disable-next-line no-console + console.warn( + `[state-machine] No transition for event "${event.type}" in state "${currentState}"`, + ) + } + return null + } + + for (const t of candidates) { + const allGuardsPass = t.guards.every(g => g.fn(context, event)) + if (!allGuardsPass) continue + + let nextContext = context + for (const r of t.reducers) { + nextContext = r.fn(nextContext, event) as C + } + + return { current: t.target, context: nextContext } + } + + if (import.meta.env.DEV) { + // eslint-disable-next-line no-console + console.warn( + `[state-machine] All guards failed for event "${event.type}" in state "${currentState}"`, + ) + } + return null +} + +/** @internal */ +export function interpret( + machine: Machine, + onChange: (service: Service) => void, + initialContext?: any, +): Service { + const context = machine.contextFactory(initialContext) + + const service: Service = { + machine: { current: machine.initial }, + context, + send: (event: SendEvent) => { + const result = applyTransition( + machine.states, + service.machine.current, + service.context, + event, + ) + if (result) { + service.machine = { current: result.current } + service.context = result.context + onChange(service) + } + }, + child: undefined, + } + + return service +} diff --git a/src/lib/state-machine/index.ts b/src/lib/state-machine/index.ts new file mode 100644 index 0000000000..d9d711d204 --- /dev/null +++ b/src/lib/state-machine/index.ts @@ -0,0 +1,75 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useReducer, useRef } from 'react' +import { applyTransition } from './core' +import type { Machine, MachineState, SendEvent, Service, StateDefinition } from './types' + +export { guard, reduce, transition, state, createMachine, interpret } from './core' +export type { + Guard, + Reducer, + TransitionDefinition, + StateDefinition, + Machine, + MachineState, + Service, + SendFunction, + SendEvent, + ContextFunction, + Transition, + Immediate, + GetMachineTransitions, +} from './types' + +type ReducerState = { current: string; context: C } + +type ReducerAction = SendEvent | { type: '__RESET__'; _initial: string; _context: C } + +function machineReducer( + machineStates: Record, + state: ReducerState, + action: ReducerAction, +): ReducerState { + if (action.type === '__RESET__' && '_initial' in action) { + return { current: action._initial, context: action._context } + } + return ( + applyTransition(machineStates, state.current, state.context, action as SendEvent) ?? state + ) +} + +/** @internal */ +export function useMachine( + machine: Machine, + initialContext?: any, +): [MachineState, (event: SendEvent) => void, Service] { + const machineRef = useRef(machine) + + const [{ current, context }, dispatch] = useReducer( + (s: ReducerState, a: ReducerAction) => machineReducer(machineRef.current.states, s, a), + undefined, + () => ({ + current: machine.initial, + context: machine.contextFactory(initialContext), + }), + ) + + if (machineRef.current !== machine) { + machineRef.current = machine + const resetContext = machine.contextFactory(initialContext) + dispatch({ type: '__RESET__', _initial: machine.initial, _context: resetContext }) + } + + const send = (event: SendEvent) => { + dispatch(event) + } + + const currentState: MachineState = { name: current, context } + const service: Service = { + machine: { current }, + context, + send, + child: undefined, + } + + return [currentState, send, service] +} diff --git a/src/lib/state-machine/types.ts b/src/lib/state-machine/types.ts new file mode 100644 index 0000000000..3e86cdb2df --- /dev/null +++ b/src/lib/state-machine/types.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/** @internal */ +export type SendEvent = { type: string; payload?: unknown } + +/** @internal */ +export type SendFunction = (event: SendEvent | { type: T }) => void + +/** @internal */ +export type Guard = { + _tag: 'guard' + fn: (ctx: C, ev: any) => boolean +} + +/** @internal */ +export type Reducer = { + _tag: 'reduce' + fn: (ctx: C, ev: any) => C +} + +/** @internal */ +export type TransitionDefinition = { + _tag: 'transition' + event: string + target: string + guards: Guard[] + reducers: Reducer[] +} + +/** @internal */ +export type Transition<_E extends string = string> = TransitionDefinition +/** @internal */ +export type Immediate<_E extends string = string> = TransitionDefinition + +/** @internal */ +export type StateDefinition = { + _tag: 'state' + transitions: Map[]> +} + +/** @internal */ +export type ContextFunction = (initialContext?: any) => C + +/** @internal */ +export type Machine = { + initial: string + states: Record + contextFactory: ContextFunction +} + +/** @internal */ +export type MachineState = { + name: string + context: C +} + +/** @internal */ +export type Service = { + machine: { current: string } + context: C + send: SendFunction + child: undefined +} + +/** @internal */ +export type GetMachineTransitions = TransitionDefinition diff --git a/src/shared/constants.ts b/src/shared/constants.ts index e8a0bcfb5b..f92b041b24 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -577,7 +577,7 @@ export const timeOffEvents = { * ``` */ export const componentEvents = { - ROBOT_MACHINE_DONE: 'done', //This is internal Robot event thrown when machine transitions to final state + ROBOT_MACHINE_DONE: 'done', ERROR: 'ERROR', CANCEL: 'CANCEL', BREADCRUMB_NAVIGATE: 'breadcrumb/navigate', diff --git a/src/types/Helpers.d.ts b/src/types/Helpers.d.ts index a173f363f1..98bb233832 100644 --- a/src/types/Helpers.d.ts +++ b/src/types/Helpers.d.ts @@ -36,11 +36,9 @@ export type MachineEventType< payload: TEventPayloads[TEventType] } -// Robot3 state machine helper types for consistent usage across the codebase -export type { Transition, Immediate } from 'robot3' +export type { Transition, Immediate } from '@/lib/state-machine' export type { EventType } from '@/shared/constants' -// Reusable type for robot3 state machine transitions that accept any EventType export type MachineTransition = Transition | Immediate //Makes specific property in the given type required From dd18d31a09382d711396f52985f4d5d25780c480 Mon Sep 17 00:00:00 2001 From: Steve Jensen Date: Mon, 24 Aug 2026 09:27:27 -0600 Subject: [PATCH 2/2] refactor: cleanup naming and migrate remaining robot3 imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename single-letter loop variables in applyTransition for clarity (t→candidate, r→reducer, t→def). Rename ApplyResult→TransitionResult. Migrate 8 files added on main that still imported from robot3/react-robot. Co-Authored-By: Claude Opus 4.6 --- .../agent-payments/AgentPaymentsFlow.tsx | 2 +- .../agentPaymentsStateMachine.ts | 2 +- .../prototypes/tax-filings/TaxFilingsFlow.tsx | 2 +- .../tax-filings/taxFilingsStateMachine.ts | 2 +- .../HistoricalPaymentFlow.tsx | 2 +- .../historicalPaymentMachine.ts | 2 +- .../Payroll/PrintChecks/PrintChecks.tsx | 3 +-- .../PrintChecks/printChecksStateMachine.tsx | 2 +- src/lib/state-machine/core.ts | 22 +++++++++---------- 9 files changed, 19 insertions(+), 20 deletions(-) diff --git a/sdk-app/src/design/prototypes/agent-payments/AgentPaymentsFlow.tsx b/sdk-app/src/design/prototypes/agent-payments/AgentPaymentsFlow.tsx index 199a17bcca..5e2b159311 100644 --- a/sdk-app/src/design/prototypes/agent-payments/AgentPaymentsFlow.tsx +++ b/sdk-app/src/design/prototypes/agent-payments/AgentPaymentsFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { agentPaymentsMachine } from './agentPaymentsStateMachine' import type { @@ -6,6 +5,7 @@ import type { AgentPaymentsFlowContextInterface, } from './AgentPaymentsFlowComponents' import { AgentPaymentsListContextual } from './AgentPaymentsFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' export const AgentPaymentsFlow = ({ companyId, onEvent }: AgentPaymentsFlowProps) => { diff --git a/sdk-app/src/design/prototypes/agent-payments/agentPaymentsStateMachine.ts b/sdk-app/src/design/prototypes/agent-payments/agentPaymentsStateMachine.ts index b324a0d740..9248fbaf90 100644 --- a/sdk-app/src/design/prototypes/agent-payments/agentPaymentsStateMachine.ts +++ b/sdk-app/src/design/prototypes/agent-payments/agentPaymentsStateMachine.ts @@ -1,10 +1,10 @@ -import { transition, reduce, state } from 'robot3' import { AgentPaymentsListContextual, AgentPaymentDetailContextual, type AgentPaymentsFlowContextInterface, } from './AgentPaymentsFlowComponents' import { agentPaymentsEvents } from './events' +import { transition, reduce, state } from '@/lib/state-machine' import type { MachineTransition } from '@/types/Helpers' const createReducer = (props: Partial) => { diff --git a/sdk-app/src/design/prototypes/tax-filings/TaxFilingsFlow.tsx b/sdk-app/src/design/prototypes/tax-filings/TaxFilingsFlow.tsx index 4f0c5f028b..ff8562a8aa 100644 --- a/sdk-app/src/design/prototypes/tax-filings/TaxFilingsFlow.tsx +++ b/sdk-app/src/design/prototypes/tax-filings/TaxFilingsFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useMemo } from 'react' import { taxFilingsMachine } from './taxFilingsStateMachine' import type { @@ -6,6 +5,7 @@ import type { TaxFilingsFlowContextInterface, } from './TaxFilingsFlowComponents' import { TaxFilingsListContextual } from './TaxFilingsFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' export const TaxFilingsFlow = ({ companyId, onEvent }: TaxFilingsFlowProps) => { diff --git a/sdk-app/src/design/prototypes/tax-filings/taxFilingsStateMachine.ts b/sdk-app/src/design/prototypes/tax-filings/taxFilingsStateMachine.ts index ac6c9da1c2..9372fe05ab 100644 --- a/sdk-app/src/design/prototypes/tax-filings/taxFilingsStateMachine.ts +++ b/sdk-app/src/design/prototypes/tax-filings/taxFilingsStateMachine.ts @@ -1,10 +1,10 @@ -import { transition, reduce, state } from 'robot3' import { taxFilingsEvents } from './events' import { TaxFilingsListContextual, TaxFilingDetailContextual, type TaxFilingsFlowContextInterface, } from './TaxFilingsFlowComponents' +import { transition, reduce, state } from '@/lib/state-machine' import type { MachineTransition } from '@/types/Helpers' const createReducer = (props: Partial) => { diff --git a/src/components/Contractor/Payments/HistoricalPaymentFlow/HistoricalPaymentFlow.tsx b/src/components/Contractor/Payments/HistoricalPaymentFlow/HistoricalPaymentFlow.tsx index cf6b3c6d9c..c4c2f17aba 100644 --- a/src/components/Contractor/Payments/HistoricalPaymentFlow/HistoricalPaymentFlow.tsx +++ b/src/components/Contractor/Payments/HistoricalPaymentFlow/HistoricalPaymentFlow.tsx @@ -1,4 +1,3 @@ -import { createMachine } from 'robot3' import { useState } from 'react' import { historicalPaymentMachine } from './historicalPaymentMachine' import { @@ -6,6 +5,7 @@ import { type HistoricalPaymentFlowContextInterface, type HistoricalPaymentFlowProps, } from './HistoricalPaymentFlowComponents' +import { createMachine } from '@/lib/state-machine' import { Flow } from '@/components/Flow/Flow' import type { FlowBreadcrumb } from '@/components/Common/FlowBreadcrumbs/FlowBreadcrumbsTypes' import { updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Contractor/Payments/HistoricalPaymentFlow/historicalPaymentMachine.ts b/src/components/Contractor/Payments/HistoricalPaymentFlow/historicalPaymentMachine.ts index e1111019dd..21a6fe869d 100644 --- a/src/components/Contractor/Payments/HistoricalPaymentFlow/historicalPaymentMachine.ts +++ b/src/components/Contractor/Payments/HistoricalPaymentFlow/historicalPaymentMachine.ts @@ -1,9 +1,9 @@ -import { reduce, state, transition } from 'robot3' import type { ContractorPaymentGroup } from '@gusto/embedded-api/models/components/contractorpaymentgroup' import { HistoricalPaymentSummaryContextual, type HistoricalPaymentFlowContextInterface, } from './HistoricalPaymentFlowComponents' +import { reduce, state, transition } from '@/lib/state-machine' import { componentEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' import { updateBreadcrumbs } from '@/helpers/breadcrumbHelpers' diff --git a/src/components/Payroll/PrintChecks/PrintChecks.tsx b/src/components/Payroll/PrintChecks/PrintChecks.tsx index ac39353d11..006e7cfdd4 100644 --- a/src/components/Payroll/PrintChecks/PrintChecks.tsx +++ b/src/components/Payroll/PrintChecks/PrintChecks.tsx @@ -1,10 +1,9 @@ -import { createMachine } from 'robot3' -import { useMachine } from 'react-robot' import { useMemo, useState } from 'react' import { PrintChecksBanner } from './PrintChecksBanner' import { printChecksMachine } from './printChecksStateMachine' import { type PrintChecksContextInterface } from './PrintChecksComponents' import type { PrintChecksProps } from './types' +import { createMachine, useMachine } from '@/lib/state-machine' import { BaseComponent } from '@/components/Base' import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext' import { FlowContext } from '@/components/Flow/useFlow' diff --git a/src/components/Payroll/PrintChecks/printChecksStateMachine.tsx b/src/components/Payroll/PrintChecks/printChecksStateMachine.tsx index 6124518352..b739f09de1 100644 --- a/src/components/Payroll/PrintChecks/printChecksStateMachine.tsx +++ b/src/components/Payroll/PrintChecks/printChecksStateMachine.tsx @@ -1,10 +1,10 @@ -import { state, transition, reduce } from 'robot3' import type { PrintChecksContextInterface } from './PrintChecksComponents' import { PrintChecksFormContextual, PrintChecksFailureContextual, PrintChecksSummaryContextual, } from './PrintChecksComponents' +import { state, transition, reduce } from '@/lib/state-machine' import { printChecksEvents } from '@/shared/constants' import type { MachineEventType, MachineTransition } from '@/types/Helpers' diff --git a/src/lib/state-machine/core.ts b/src/lib/state-machine/core.ts index a8c2669de0..416ba92502 100644 --- a/src/lib/state-machine/core.ts +++ b/src/lib/state-machine/core.ts @@ -38,12 +38,12 @@ export function transition( /** @internal */ export function state<_T = unknown>(...transitions: TransitionDefinition[]): StateDefinition { const transitionMap = new Map() - for (const t of transitions) { - const existing = transitionMap.get(t.event) + for (const def of transitions) { + const existing = transitionMap.get(def.event) if (existing) { - existing.push(t) + existing.push(def) } else { - transitionMap.set(t.event, [t]) + transitionMap.set(def.event, [def]) } } return { _tag: 'state', transitions: transitionMap } @@ -59,7 +59,7 @@ export function createMachine( } /** @internal */ -export type ApplyResult = { current: string; context: C } +export type TransitionResult = { current: string; context: C } /** @internal */ export function applyTransition( @@ -67,7 +67,7 @@ export function applyTransition( currentState: string, context: C, event: SendEvent, -): ApplyResult | null { +): TransitionResult | null { const stateDef = states[currentState] if (!stateDef) { if (import.meta.env.DEV) { @@ -88,16 +88,16 @@ export function applyTransition( return null } - for (const t of candidates) { - const allGuardsPass = t.guards.every(g => g.fn(context, event)) + for (const candidate of candidates) { + const allGuardsPass = candidate.guards.every(g => g.fn(context, event)) if (!allGuardsPass) continue let nextContext = context - for (const r of t.reducers) { - nextContext = r.fn(nextContext, event) as C + for (const reducer of candidate.reducers) { + nextContext = reducer.fn(nextContext, event) as C } - return { current: t.target, context: nextContext } + return { current: candidate.target, context: nextContext } } if (import.meta.env.DEV) {