Last active
April 7, 2020 08:47
-
-
Save austin-sa-wang/919afbb9167d9cf6727ce169d2a91b59 to your computer and use it in GitHub Desktop.
onboarding wizard design for simplicity and flexbility
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
/** | |
* Simple wizard | |
* design philosophy: simplicity, wysiwyg, isolated responsibilities | |
* key design decision: each wizard step is aware that it's part of a wizard | |
* upside: simple and flexible | |
* downside: more boilerplate | |
* case-by-case: when we need to add or rearrange the steps, we also need to modify existing steps (smell: Shotgun Surgery) | |
* however, this is aligned with the key design decision | |
* | |
* alternative key design: opposite - isolated steps that are unaware of the fact that they're part of a wizard | |
* we plug components into a grand wizard framework. handle all business logics for the steps inside the wizard | |
* updside: minimal boilerplate | |
* downside: rigid, all logics is coupled and wizard needs to implement everything for the steps | |
* case-by-case: super easy to use in some situations. For example, an instruction wizard without any business logic | |
*/ | |
// use this for cross step data store if necessary. Isolated. Not integrated into wizard | |
const OnboardingWizardStore = React.createContext({}) | |
// ------------------- isolated Step definitions -------------------------- | |
const WizardFirstStep = (props) => { | |
const [state, setState] = useState({}) | |
const saveFirstStepAndProceed = () => { | |
doBusinessStuffSuchAsSave(firstPageState) | |
props.onNext() | |
} | |
return ( | |
<WizardStepLayout> | |
<input onChange={setState}>{{state}}</input> | |
// manually define the buttons is definitely boilerplate, but it's the simplest to maintain and update | |
<WizardStepLayoutButtons> | |
<button onClick={saveFirstStepAndProceed}>next</button> | |
<button onClick={onNext}>skip</button> | |
</WizardStepLayoutButtons> | |
</WizardStepLayout> | |
) | |
} | |
const WizardSecondStep = (props) => { | |
const onboardingWizardStore = React.useContext(OnboardingWizardStore) // use this for cross step data store | |
const [state, setState] = useState({}) | |
const saveFirstStepAndProceed = () => { | |
doBusinessStuffSuchAsSave(firstPageState) | |
props.onNext() | |
} | |
return ( | |
<WizardStepLayout> | |
<input onChange={setState}>{{state}}</input> | |
<WizardStepLayoutButtons> | |
<button onClick={saveFirstStepAndProceed()}>next</button> | |
<button onClick={onBack}>back</button> // we can change the buttons easily | |
</WizardStepLayoutButtons> | |
</WizardStepLayout> | |
) | |
} | |
// ----------------- setup of wizard itself. the glue code ----------- | |
function OnboardingWizard () { | |
// wizard is only responsbile of tracking and navigating the steps, nothing more. | |
const { | |
currentStep, | |
setCurrentStep, | |
goToNextStep, | |
goToPreviousStep | |
} = useWizard({ // implementation of useWizard is omitted. | |
steps: [STEP.INFO, STEP.CONNECT, STEP.INVITE, STEP.INVITE_CUSTOMEMAIL]<StepsEnum> | |
}) | |
const WizardContent = () => { | |
if (currentStep === 1) | |
return <WizardFirstStep onNext={goToNextStep} /> | |
if (currentStep === 2) { | |
return <WizardSecondStep onNext={goToNextStep} /> | |
} | |
return ( | |
<OnboardingWizardStore.Provider> // use this for cross step data store | |
<WizardContent /> | |
</OnboardingWizardStore.Provider> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment