-
Notifications
You must be signed in to change notification settings - Fork 376
next(Wizard): Allow for WizardStep to better control state from props entry-point, include index #8218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
next(Wizard): Allow for WizardStep to better control state from props entry-point, include index #8218
Changes from 1 commit
0ffa3fd
8f63e91
ade6abe
eac5447
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,15 @@ | ||
| import React from 'react'; | ||
|
|
||
| import { isCustomWizardFooter, isWizardParentStep, WizardControlStep, WizardFooterType } from './types'; | ||
| import { getCurrentStep } from './utils'; | ||
| import { isCustomWizardFooter, WizardControlStep, WizardFooterType } from './types'; | ||
| import { getActiveStep } from './utils'; | ||
| import { useGetMergedSteps } from './hooks/useGetMergedSteps'; | ||
| import { WizardFooter, WizardFooterProps } from './WizardFooter'; | ||
|
|
||
| export interface WizardContextProps { | ||
| /** List of steps */ | ||
| steps: WizardControlStep[]; | ||
| /** Current step */ | ||
| currentStep: WizardControlStep; | ||
| /** Current step index */ | ||
| currentStepIndex: number; | ||
| activeStep: WizardControlStep; | ||
| /** Footer element */ | ||
| footer: React.ReactElement; | ||
| /** Navigate to the next step */ | ||
|
|
@@ -31,17 +30,16 @@ export interface WizardContextProps { | |
| getStep: (stepId: number | string) => WizardControlStep; | ||
| /** Set step by ID */ | ||
| setStep: (step: Pick<WizardControlStep, 'id'> & Partial<WizardControlStep>) => void; | ||
| /** Toggle step visibility by ID */ | ||
| toggleStep: (stepId: number | string, isHidden: boolean) => void; | ||
| /** Set multiple steps */ | ||
|
||
| setSteps: React.Dispatch<React.SetStateAction<WizardControlStep[]>>; | ||
| } | ||
|
|
||
| export const WizardContext = React.createContext({} as WizardContextProps); | ||
|
|
||
| export interface WizardContextProviderProps { | ||
| steps: WizardControlStep[]; | ||
| currentStepIndex: number; | ||
| activeStepIndex: number; | ||
| footer: WizardFooterType; | ||
| isStepVisitRequired: boolean; | ||
| children: React.ReactElement; | ||
| onNext(steps: WizardControlStep[]): void; | ||
| onBack(steps: WizardControlStep[]): void; | ||
|
|
@@ -54,8 +52,7 @@ export interface WizardContextProviderProps { | |
| export const WizardContextProvider: React.FunctionComponent<WizardContextProviderProps> = ({ | ||
| steps: initialSteps, | ||
| footer: initialFooter, | ||
| currentStepIndex, | ||
| isStepVisitRequired, | ||
| activeStepIndex, | ||
| children, | ||
| onNext, | ||
| onBack, | ||
|
|
@@ -68,35 +65,38 @@ export const WizardContextProvider: React.FunctionComponent<WizardContextProvide | |
| const [currentFooter, setCurrentFooter] = React.useState( | ||
| typeof initialFooter !== 'function' ? initialFooter : undefined | ||
| ); | ||
| const currentStep = getCurrentStep(steps, currentStepIndex); | ||
| const mergedSteps = useGetMergedSteps(initialSteps, steps); | ||
| const activeStep = getActiveStep(mergedSteps, activeStepIndex); | ||
|
|
||
| const goToNextStep = React.useCallback(() => onNext(steps), [onNext, steps]); | ||
| const goToPrevStep = React.useCallback(() => onBack(steps), [onBack, steps]); | ||
| const goToNextStep = React.useCallback(() => onNext(mergedSteps), [onNext, mergedSteps]); | ||
| const goToPrevStep = React.useCallback(() => onBack(mergedSteps), [onBack, mergedSteps]); | ||
|
|
||
| const footer = React.useMemo(() => { | ||
| const wizardFooter = currentFooter || initialFooter; | ||
| const wizardFooter = activeStep?.footer || currentFooter || initialFooter; | ||
|
|
||
| if (isCustomWizardFooter(wizardFooter)) { | ||
| const customFooter = wizardFooter; | ||
|
|
||
| return typeof customFooter === 'function' | ||
| ? customFooter(currentStep, goToNextStep, goToPrevStep, onClose) | ||
| ? customFooter(activeStep, goToNextStep, goToPrevStep, onClose) | ||
| : customFooter; | ||
| } | ||
|
|
||
| return ( | ||
| <WizardFooter | ||
| currentStep={currentStep} | ||
| activeStep={activeStep} | ||
| onNext={goToNextStep} | ||
| onBack={goToPrevStep} | ||
| onClose={onClose} | ||
| isBackDisabled={currentStep?.id === steps[0]?.id} | ||
| isBackDisabled={activeStep?.id === mergedSteps[0]?.id} | ||
| {...wizardFooter} | ||
| /> | ||
| ); | ||
| }, [currentFooter, initialFooter, currentStep, goToNextStep, goToPrevStep, onClose, steps]); | ||
| }, [currentFooter, initialFooter, activeStep, goToNextStep, goToPrevStep, onClose, mergedSteps]); | ||
|
|
||
| const getStep = React.useCallback((stepId: string | number) => steps.find(step => step.id === stepId), [steps]); | ||
| const getStep = React.useCallback((stepId: string | number) => mergedSteps.find(step => step.id === stepId), [ | ||
| mergedSteps | ||
| ]); | ||
|
|
||
| const setStep = React.useCallback( | ||
| (step: Pick<WizardControlStep, 'id'> & Partial<WizardControlStep>) => | ||
|
|
@@ -112,62 +112,22 @@ export const WizardContextProvider: React.FunctionComponent<WizardContextProvide | |
| [] | ||
| ); | ||
|
|
||
| const toggleStep = React.useCallback( | ||
| (stepId: string | number, isHidden: boolean) => | ||
| setSteps(prevSteps => { | ||
| let stepToHide: WizardControlStep; | ||
|
|
||
| return prevSteps.map(prevStep => { | ||
| if (prevStep.id === stepId) { | ||
| // Don't hide the currently active step or its parent (if a sub-step). | ||
| if ( | ||
| isHidden && | ||
| (currentStep.id === prevStep.id || | ||
| (isWizardParentStep(prevStep) && prevStep.subStepIds.includes(currentStep.id))) | ||
| ) { | ||
| // eslint-disable-next-line no-console | ||
| console.error('Wizard: Unable to hide the current step or its parent.'); | ||
| return prevStep; | ||
| } | ||
|
|
||
| stepToHide = { ...prevStep, isHidden }; | ||
| return stepToHide; | ||
| } | ||
|
|
||
| // When isStepVisitRequired is enabled, if the step was previously hidden and not visited yet, | ||
| // when it is shown, all steps beyond it should be disabled to ensure it is visited. | ||
| if ( | ||
| isStepVisitRequired && | ||
| stepToHide?.isHidden === false && | ||
| !stepToHide?.isVisited && | ||
| prevSteps.indexOf(stepToHide) < prevSteps.indexOf(prevStep) | ||
| ) { | ||
| return { ...prevStep, isVisited: false }; | ||
| } | ||
|
|
||
| return prevStep; | ||
| }); | ||
| }), | ||
| [currentStep.id, isStepVisitRequired] | ||
| ); | ||
|
|
||
| return ( | ||
| <WizardContext.Provider | ||
| value={{ | ||
| steps, | ||
| currentStep, | ||
| currentStepIndex, | ||
| steps: mergedSteps, | ||
| activeStep, | ||
| footer, | ||
| onClose, | ||
| getStep, | ||
| setStep, | ||
| toggleStep, | ||
| setSteps, | ||
| setFooter: setCurrentFooter, | ||
| onNext: goToNextStep, | ||
| onBack: goToPrevStep, | ||
| goToStepById: React.useCallback(id => goToStepById(steps, id), [goToStepById, steps]), | ||
| goToStepByName: React.useCallback(name => goToStepByName(steps, name), [goToStepByName, steps]), | ||
| goToStepByIndex: React.useCallback(index => goToStepByIndex(steps, index), [goToStepByIndex, steps]) | ||
| goToStepById: React.useCallback(id => goToStepById(mergedSteps, id), [goToStepById, mergedSteps]), | ||
| goToStepByName: React.useCallback(name => goToStepByName(mergedSteps, name), [goToStepByName, mergedSteps]), | ||
| goToStepByIndex: React.useCallback(index => goToStepByIndex(mergedSteps, index), [goToStepByIndex, mergedSteps]) | ||
| }} | ||
| > | ||
| {children} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌