11import React from 'react' ;
2- import findLastIndex from 'lodash/findLastIndex' ;
32
43import { css } from '@patternfly/react-styles' ;
54import styles from '@patternfly/react-styles/css/components/Wizard/wizard' ;
65
76import {
87 isWizardParentStep ,
9- WizardNavStepFunction ,
10- WizardControlStep ,
8+ WizardStepType ,
119 isCustomWizardNav ,
1210 WizardFooterType ,
13- WizardNavType
11+ WizardNavType ,
12+ WizardStepChangeScope
1413} from './types' ;
15- import { buildSteps , normalizeNavStep } from './utils' ;
14+ import { buildSteps } from './utils' ;
1615import { useWizardContext , WizardContextProvider } from './WizardContext' ;
17- import { WizardStepProps } from './WizardStep' ;
1816import { WizardToggle } from './WizardToggle' ;
1917import { WizardNavInternal } from './WizardNavInternal' ;
2018
@@ -25,7 +23,7 @@ import { WizardNavInternal } from './WizardNavInternal';
2523
2624export interface WizardProps extends React . HTMLProps < HTMLDivElement > {
2725 /** Step components */
28- children : React . ReactElement < WizardStepProps > | React . ReactElement < WizardStepProps > [ ] ;
26+ children : React . ReactNode | React . ReactNode [ ] ;
2927 /** Wizard header */
3028 header ?: React . ReactNode ;
3129 /** Wizard footer */
@@ -40,18 +38,21 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
4038 width ?: number | string ;
4139 /** Custom height of the wizard */
4240 height ?: number | string ;
43- /** Disables navigation items that haven't been visited. Defaults to false */
44- isStepVisitRequired ?: boolean ;
45- /** Callback function when a step in the navigation is clicked */
46- onNavByIndex ?: WizardNavStepFunction ;
47- /** Callback function after next button is clicked */
48- onNext ?: WizardNavStepFunction ;
49- /** Callback function after back button is clicked */
50- onBack ?: WizardNavStepFunction ;
41+ /** Disables steps that haven't been visited. Defaults to false. */
42+ isVisitRequired ?: boolean ;
43+ /** Progressively shows steps, where all steps following the active step are hidden. Defaults to false. */
44+ isProgressive ?: boolean ;
45+ /** Callback function when navigating between steps */
46+ onStepChange ?: (
47+ event : React . MouseEvent < HTMLButtonElement > ,
48+ currentStep : WizardStepType ,
49+ prevStep : WizardStepType ,
50+ scope : WizardStepChangeScope
51+ ) => void | Promise < void > ;
5152 /** Callback function to save at the end of the wizard, if not specified uses onClose */
52- onSave ?: ( ) => void | Promise < void > ;
53+ onSave ?: ( event : React . MouseEvent < HTMLButtonElement > ) => void | Promise < void > ;
5354 /** Callback function to close the wizard */
54- onClose ?: ( ) => void ;
55+ onClose ?: ( event : React . MouseEvent < HTMLButtonElement > ) => void ;
5556}
5657
5758export const Wizard = ( {
@@ -63,48 +64,61 @@ export const Wizard = ({
6364 header,
6465 nav,
6566 startIndex = 1 ,
66- isStepVisitRequired = false ,
67- onNavByIndex,
68- onNext,
69- onBack,
67+ isVisitRequired = false ,
68+ isProgressive = false ,
69+ onStepChange,
7070 onSave,
7171 onClose,
7272 ...wrapperProps
7373} : WizardProps ) => {
7474 const [ activeStepIndex , setActiveStepIndex ] = React . useState ( startIndex ) ;
7575 const initialSteps = buildSteps ( children ) ;
76+ const firstStepRef = React . useRef ( initialSteps [ startIndex - 1 ] ) ;
7677
77- const goToNextStep = ( steps : WizardControlStep [ ] = initialSteps ) => {
78- const newStepIndex = steps . find ( step => step . index > activeStepIndex && ! step . isHidden && ! isWizardParentStep ( step ) )
79- ?. index ;
78+ // When the startIndex maps to a parent step, focus on the first sub-step
79+ React . useEffect ( ( ) => {
80+ if ( isWizardParentStep ( firstStepRef . current ) ) {
81+ setActiveStepIndex ( startIndex + 1 ) ;
82+ }
83+ } , [ startIndex ] ) ;
84+
85+ const goToNextStep = ( event : React . MouseEvent < HTMLButtonElement > , steps : WizardStepType [ ] = initialSteps ) => {
86+ const newStep = steps . find (
87+ step => step . index > activeStepIndex && ! step . isHidden && ! step . isDisabled && ! isWizardParentStep ( step )
88+ ) ;
8089
81- if ( activeStepIndex >= steps . length || ! newStepIndex ) {
82- return onSave ? onSave ( ) : onClose ?.( ) ;
90+ if ( activeStepIndex >= steps . length || ! newStep ?. index ) {
91+ return onSave ? onSave ( event ) : onClose ?.( event ) ;
8392 }
8493
8594 const currStep = isWizardParentStep ( steps [ activeStepIndex ] ) ? steps [ activeStepIndex + 1 ] : steps [ activeStepIndex ] ;
8695 const prevStep = steps [ activeStepIndex - 1 ] ;
8796
88- setActiveStepIndex ( newStepIndex ) ;
89- return onNext ?.( normalizeNavStep ( currStep ) , normalizeNavStep ( prevStep ) ) ;
97+ setActiveStepIndex ( newStep ?. index ) ;
98+ onStepChange ?.( event , currStep , prevStep , WizardStepChangeScope . Next ) ;
9099 } ;
91100
92- const goToPrevStep = ( steps : WizardControlStep [ ] = initialSteps ) => {
93- const newStepIndex =
94- findLastIndex (
95- steps ,
96- ( step : WizardControlStep ) => step . index < activeStepIndex && ! step . isHidden && ! isWizardParentStep ( step )
97- ) + 1 ;
101+ const goToPrevStep = ( event : React . MouseEvent < HTMLButtonElement > , steps : WizardStepType [ ] = initialSteps ) => {
102+ const newStep = [ ...steps ]
103+ . reverse ( )
104+ . find (
105+ ( step : WizardStepType ) =>
106+ step . index < activeStepIndex && ! step . isHidden && ! step . isDisabled && ! isWizardParentStep ( step )
107+ ) ;
98108 const currStep = isWizardParentStep ( steps [ activeStepIndex - 2 ] )
99109 ? steps [ activeStepIndex - 3 ]
100110 : steps [ activeStepIndex - 2 ] ;
101111 const prevStep = steps [ activeStepIndex - 1 ] ;
102112
103- setActiveStepIndex ( newStepIndex ) ;
104- return onBack ?.( normalizeNavStep ( currStep ) , normalizeNavStep ( prevStep ) ) ;
113+ setActiveStepIndex ( newStep ?. index ) ;
114+ onStepChange ?.( event , currStep , prevStep , WizardStepChangeScope . Back ) ;
105115 } ;
106116
107- const goToStepByIndex = ( steps : WizardControlStep [ ] = initialSteps , index : number ) => {
117+ const goToStepByIndex = (
118+ event : React . MouseEvent < HTMLButtonElement > ,
119+ steps : WizardStepType [ ] = initialSteps ,
120+ index : number
121+ ) => {
108122 const lastStepIndex = steps . length + 1 ;
109123
110124 // Handle index when out of bounds or hidden
@@ -118,25 +132,25 @@ export const Wizard = ({
118132 const prevStep = steps [ activeStepIndex - 1 ] ;
119133
120134 setActiveStepIndex ( index ) ;
121- return onNavByIndex ?.( normalizeNavStep ( currStep ) , normalizeNavStep ( prevStep ) ) ;
135+ onStepChange ?.( event , currStep , prevStep , WizardStepChangeScope . Nav ) ;
122136 } ;
123137
124- const goToStepById = ( steps : WizardControlStep [ ] = initialSteps , id : number | string ) => {
138+ const goToStepById = ( steps : WizardStepType [ ] = initialSteps , id : number | string ) => {
125139 const step = steps . find ( step => step . id === id ) ;
126140 const stepIndex = step ?. index ;
127141 const lastStepIndex = steps . length + 1 ;
128142
129- if ( stepIndex > 0 && stepIndex < lastStepIndex && ! step . isHidden ) {
143+ if ( stepIndex > 0 && stepIndex < lastStepIndex && ! step . isDisabled && ! step . isHidden ) {
130144 setActiveStepIndex ( stepIndex ) ;
131145 }
132146 } ;
133147
134- const goToStepByName = ( steps : WizardControlStep [ ] = initialSteps , name : string ) => {
148+ const goToStepByName = ( steps : WizardStepType [ ] = initialSteps , name : string ) => {
135149 const step = steps . find ( step => step . name === name ) ;
136150 const stepIndex = step ?. index ;
137151 const lastStepIndex = steps . length + 1 ;
138152
139- if ( stepIndex > 0 && stepIndex < lastStepIndex && ! step . isHidden ) {
153+ if ( stepIndex > 0 && stepIndex < lastStepIndex && ! step . isDisabled && ! step . isHidden ) {
140154 setActiveStepIndex ( stepIndex ) ;
141155 }
142156 } ;
@@ -162,13 +176,17 @@ export const Wizard = ({
162176 { ...wrapperProps }
163177 >
164178 { header }
165- < WizardInternal nav = { nav } isStepVisitRequired = { isStepVisitRequired } />
179+ < WizardInternal nav = { nav } isVisitRequired = { isVisitRequired } isProgressive = { isProgressive } />
166180 </ div >
167181 </ WizardContextProvider >
168182 ) ;
169183} ;
170184
171- const WizardInternal = ( { nav, isStepVisitRequired } : Pick < WizardProps , 'nav' | 'isStepVisitRequired' > ) => {
185+ const WizardInternal = ( {
186+ nav,
187+ isVisitRequired,
188+ isProgressive
189+ } : Pick < WizardProps , 'nav' | 'isVisitRequired' | 'isProgressive' > ) => {
172190 const { activeStep, steps, footer, goToStepByIndex } = useWizardContext ( ) ;
173191 const [ isNavExpanded , setIsNavExpanded ] = React . useState ( false ) ;
174192
@@ -177,8 +195,15 @@ const WizardInternal = ({ nav, isStepVisitRequired }: Pick<WizardProps, 'nav' |
177195 return typeof nav === 'function' ? nav ( isNavExpanded , steps , activeStep , goToStepByIndex ) : nav ;
178196 }
179197
180- return < WizardNavInternal nav = { nav } isNavExpanded = { isNavExpanded } isStepVisitRequired = { isStepVisitRequired } /> ;
181- } , [ activeStep , isStepVisitRequired , goToStepByIndex , isNavExpanded , nav , steps ] ) ;
198+ return (
199+ < WizardNavInternal
200+ nav = { nav }
201+ isNavExpanded = { isNavExpanded }
202+ isVisitRequired = { isVisitRequired }
203+ isProgressive = { isProgressive }
204+ />
205+ ) ;
206+ } , [ activeStep , isVisitRequired , isProgressive , goToStepByIndex , isNavExpanded , nav , steps ] ) ;
182207
183208 return (
184209 < WizardToggle
0 commit comments