Skip to content

Instantly share code, notes, and snippets.

@filipemoraisjorge
Created June 7, 2021 19:16
Show Gist options
  • Select an option

  • Save filipemoraisjorge/348cbb9f3be3b5623d35b67acf0d05ba to your computer and use it in GitHub Desktop.

Select an option

Save filipemoraisjorge/348cbb9f3be3b5623d35b67acf0d05ba to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
/**
* 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