Created
June 7, 2021 19:16
-
-
Save filipemoraisjorge/348cbb9f3be3b5623d35b67acf0d05ba to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * CONSTANTS | |
| **/ | |
| const steps = { | |
| DEALER: 'DEALER', | |
| VEHICLE: 'VEHICLE', | |
| SERVICE: 'SERVICE', | |
| TIMESLOT: 'TIMESLOT', | |
| SUMMARY: 'SUMMARY', | |
| }; | |
| const stepFlow = [ | |
| steps.VEHICLE, | |
| steps.DEALER, | |
| steps.SERVICE, | |
| steps.TIMESLOT, | |
| steps.SUMMARY, | |
| ]; | |
| /** | |
| * VALIDITY FUNCTION | |
| **/ | |
| const random = (rate, left = true, right = false) => Math.random() <= rate ? left : right; | |
| const mockValidityFn = () => | |
| random( | |
| 1, | |
| Promise.resolve(random(0.95)), | |
| Promise.reject() | |
| ); | |
| const validityFunctions = { | |
| [steps.VEHICLE]: mockValidityFn, | |
| [steps.DEALER]: mockValidityFn, | |
| [steps.SERVICE]: mockValidityFn, | |
| [steps.TIMESLOT]: mockValidityFn, | |
| [steps.SUMMARY]: mockValidityFn, | |
| }; | |
| /************************************ | |
| * | |
| * STEP actor | |
| * | |
| ************************************/ | |
| const StepState = { | |
| VALIDATING: 'VALIDATING', | |
| VALID:'VALID', | |
| INVALID: 'INVALID', | |
| ERROR: 'ERROR', | |
| } | |
| const StepEvent = { | |
| VALIDATE: 'VALIDATE', | |
| } | |
| const StepServices = { | |
| DO_VALIDATION: 'DO_VALIDATION' | |
| } | |
| const createStepMachine = (stepName, validityFn) => Machine({ | |
| initial: StepState.VALIDATING, | |
| id: stepName, | |
| context: { | |
| validity: false, | |
| error: undefined, | |
| }, | |
| states: { | |
| [StepState.VALIDATING]: { | |
| invoke: { | |
| src: validityFn, | |
| // src: stepServices.DO_VALIDATION, | |
| onDone: [ | |
| { | |
| target: StepState.VALID, | |
| cond: (context, event) => !!event?.data, | |
| actions: assign({ validity: (_, event) => event.data }) | |
| }, { | |
| target: StepState.INVALID, | |
| cond: (context, event) => !event?.data, | |
| actions: assign({ validity: (_, event) => event.data }) | |
| } | |
| ], | |
| onError: { | |
| target: StepState.ERROR, | |
| actions: assign({ error: (_, event) => event.type }) | |
| } | |
| } | |
| }, | |
| [StepState.VALID]: { | |
| on: { | |
| [StepEvent.VALIDATE]: StepState.VALIDATING | |
| } | |
| }, | |
| [StepState.INVALID]: { | |
| on: { | |
| [StepEvent.VALIDATE]: StepState.VALIDATING | |
| } | |
| }, | |
| [StepState.ERROR]: { | |
| on: { | |
| [StepEvent.VALIDATE]: StepState.VALIDATING | |
| } | |
| }, | |
| } | |
| }, { | |
| services: { | |
| [StepServices.DO_VALIDATION]: validityFn | |
| } | |
| }); | |
| /************************************ | |
| * | |
| * WIZARD actor | |
| * | |
| ************************************/ | |
| const WizardState = { | |
| INIT: 'INIT', | |
| EDIT: 'EDIT', | |
| SUBMITTING: 'SUBMITTING', | |
| REVIEW: 'REVIEW', | |
| COMPLETING: 'COMPLETING', | |
| DONE: 'DONE' | |
| }; | |
| const WizardEvent = { | |
| NEXT: 'NEXT', | |
| PREV: 'PREV', | |
| TO: 'TO', | |
| SUBMIT: 'SUBMIT', | |
| SUBMITTED: 'SUBMITTED', | |
| COMPLETE: 'COMPLETE', | |
| COMPLETED: 'COMPLETED' | |
| }; | |
| const BookingEvent = { | |
| VALIDATE_DATA: 'VALIDATE_DATA', | |
| RESERVE: 'RESERVE', | |
| CONFIRM: 'CONFIRM' | |
| }; | |
| const createWizardMachine = (stepFlow, validityFns) => { | |
| const spawnStepActors = | |
| Object.fromEntries( | |
| Object.values(stepFlow) | |
| .map( | |
| stepName => [stepName, spawn(createStepMachine(stepName, validityFns[stepName]), { sync: true })] | |
| ) | |
| ); | |
| const finalStepIndex = stepFlow.length - 1; | |
| return Machine({ | |
| id: 'wizard', | |
| initial: WizardState.INIT, | |
| context: { | |
| currentStepIndex: 0, | |
| currentStep: stepFlow[0], | |
| steps: undefined | |
| }, | |
| states: { | |
| [WizardState.INIT]: { | |
| on: { | |
| '': { | |
| actions: assign({ steps: () => spawnStepActors }), | |
| target: WizardState.EDIT | |
| } | |
| }, | |
| always: { | |
| actions: assign({ steps: () => spawnStepActors }), | |
| target: WizardState.EDIT | |
| } | |
| }, | |
| [WizardState.EDIT]: { | |
| on: { | |
| [WizardEvent.PREV]: { | |
| actions: assign({ | |
| currentStepIndex: ({ currentStepIndex }) => currentStepIndex - 1, | |
| currentStep: ({ currentStepIndex }) => stepFlow[currentStepIndex - 1] | |
| }), | |
| cond: ({ currentStepIndex }) => currentStepIndex > 0 | |
| }, | |
| [WizardEvent.NEXT]: { | |
| actions: assign({ | |
| currentStepIndex: ({ currentStepIndex }) => currentStepIndex + 1, | |
| currentStep: ({ currentStepIndex }) => stepFlow[currentStepIndex + 1] | |
| }), | |
| cond: ({ currentStepIndex }) => currentStepIndex < finalStepIndex | |
| }, | |
| [WizardEvent.TO]: { | |
| actions: assign({ currentStepIndex: | |
| (_, { stepIndex }) => stepIndex | |
| }), | |
| cond: ({ currentStepIndex }, { stepIndex }) => ( | |
| stepIndex >= 0 && | |
| stepIndex <= finalStepIndex && | |
| currentStepIndex !== stepIndex | |
| ) | |
| }, | |
| [WizardEvent.SUBMIT]: { | |
| actions: [ | |
| sendParent(BookingEvent.VALIDATE_DATA), | |
| sendParent(BookingEvent.RESERVE), | |
| ], | |
| target: WizardState.SUBMITTING, | |
| cond: ({ currentStepIndex }) => currentStepIndex === finalStepIndex | |
| } | |
| } | |
| }, | |
| [WizardState.SUBMITTING]: { | |
| on: { | |
| [WizardEvent.SUBMITTED]: WizardState.REVIEW | |
| } | |
| }, | |
| [WizardState.REVIEW]: { | |
| on: { | |
| [WizardEvent.TO]: { | |
| target: WizardState.EDIT, | |
| actions: assign({ currentStepIndex: | |
| (_, { stepIndex }) => stepIndex | |
| }), | |
| cond: ({ currentStepIndex }, { stepIndex }) => ( | |
| stepIndex >= 0 && | |
| stepIndex <= finalStepIndex && | |
| currentStepIndex !== stepIndex | |
| ) | |
| }, | |
| [WizardEvent.COMPLETE]: { | |
| actions: sendParent(BookingEvent.CONFIRM), | |
| target: WizardState.COMPLETING | |
| } | |
| } | |
| }, | |
| [WizardState.COMPLETING]: { | |
| on: { | |
| [WizardEvent.COMPLETED]: WizardState.DONE | |
| } | |
| }, | |
| [WizardState.DONE]: { type: 'final' } | |
| } | |
| }); | |
| }; | |
| createWizardMachine(stepFlow, validityFunctions) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment