@patternfly/react-core 4.249.0 → 4.250.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/esm/next/components/Select/Select.d.ts +28 -0
  3. package/dist/esm/next/components/Select/Select.d.ts.map +1 -0
  4. package/dist/esm/next/components/Select/Select.js +62 -0
  5. package/dist/esm/next/components/Select/Select.js.map +1 -0
  6. package/dist/esm/next/components/Select/SelectGroup.d.ts +12 -0
  7. package/dist/esm/next/components/Select/SelectGroup.d.ts.map +1 -0
  8. package/dist/esm/next/components/Select/SelectGroup.js +10 -0
  9. package/dist/esm/next/components/Select/SelectGroup.js.map +1 -0
  10. package/dist/esm/next/components/Select/SelectList.d.ts +10 -0
  11. package/dist/esm/next/components/Select/SelectList.d.ts.map +1 -0
  12. package/dist/esm/next/components/Select/SelectList.js +10 -0
  13. package/dist/esm/next/components/Select/SelectList.js.map +1 -0
  14. package/dist/esm/next/components/Select/SelectOption.d.ts +20 -0
  15. package/dist/esm/next/components/Select/SelectOption.d.ts.map +1 -0
  16. package/dist/esm/next/components/Select/SelectOption.js +10 -0
  17. package/dist/esm/next/components/Select/SelectOption.js.map +1 -0
  18. package/dist/esm/next/components/Select/index.d.ts +5 -0
  19. package/dist/esm/next/components/Select/index.d.ts.map +1 -0
  20. package/dist/esm/next/components/Select/index.js +5 -0
  21. package/dist/esm/next/components/Select/index.js.map +1 -0
  22. package/dist/esm/next/components/Wizard/Wizard.d.ts +9 -7
  23. package/dist/esm/next/components/Wizard/Wizard.d.ts.map +1 -1
  24. package/dist/esm/next/components/Wizard/Wizard.js +62 -48
  25. package/dist/esm/next/components/Wizard/Wizard.js.map +1 -1
  26. package/dist/esm/next/components/Wizard/WizardBody.d.ts +2 -2
  27. package/dist/esm/next/components/Wizard/WizardBody.d.ts.map +1 -1
  28. package/dist/esm/next/components/Wizard/WizardBody.js +1 -1
  29. package/dist/esm/next/components/Wizard/WizardBody.js.map +1 -1
  30. package/dist/esm/next/components/Wizard/WizardContext.d.ts +21 -20
  31. package/dist/esm/next/components/Wizard/WizardContext.d.ts.map +1 -1
  32. package/dist/esm/next/components/Wizard/WizardContext.js +63 -32
  33. package/dist/esm/next/components/Wizard/WizardContext.js.map +1 -1
  34. package/dist/esm/next/components/Wizard/WizardFooter.d.ts +20 -6
  35. package/dist/esm/next/components/Wizard/WizardFooter.d.ts.map +1 -1
  36. package/dist/esm/next/components/Wizard/WizardFooter.js +13 -4
  37. package/dist/esm/next/components/Wizard/WizardFooter.js.map +1 -1
  38. package/dist/esm/next/components/Wizard/WizardHeader.d.ts +1 -1
  39. package/dist/esm/next/components/Wizard/WizardHeader.d.ts.map +1 -1
  40. package/dist/esm/next/components/Wizard/WizardHeader.js +2 -2
  41. package/dist/esm/next/components/Wizard/WizardHeader.js.map +1 -1
  42. package/dist/esm/next/components/Wizard/WizardNav.d.ts +5 -5
  43. package/dist/esm/next/components/Wizard/WizardNav.d.ts.map +1 -1
  44. package/dist/esm/next/components/Wizard/WizardNav.js +3 -4
  45. package/dist/esm/next/components/Wizard/WizardNav.js.map +1 -1
  46. package/dist/esm/next/components/Wizard/WizardNavInternal.d.ts +14 -0
  47. package/dist/esm/next/components/Wizard/WizardNavInternal.d.ts.map +1 -0
  48. package/dist/esm/next/components/Wizard/WizardNavInternal.js +49 -0
  49. package/dist/esm/next/components/Wizard/WizardNavInternal.js.map +1 -0
  50. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts +12 -8
  51. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts.map +1 -1
  52. package/dist/esm/next/components/Wizard/WizardNavItem.js +23 -12
  53. package/dist/esm/next/components/Wizard/WizardNavItem.js.map +1 -1
  54. package/dist/esm/next/components/Wizard/WizardStep.d.ts +22 -6
  55. package/dist/esm/next/components/Wizard/WizardStep.d.ts.map +1 -1
  56. package/dist/esm/next/components/Wizard/WizardStep.js +20 -1
  57. package/dist/esm/next/components/Wizard/WizardStep.js.map +1 -1
  58. package/dist/esm/next/components/Wizard/WizardToggle.d.ts +9 -15
  59. package/dist/esm/next/components/Wizard/WizardToggle.d.ts.map +1 -1
  60. package/dist/esm/next/components/Wizard/WizardToggle.js +15 -50
  61. package/dist/esm/next/components/Wizard/WizardToggle.js.map +1 -1
  62. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts +2 -2
  63. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -1
  64. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js +3 -3
  65. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js.map +1 -1
  66. package/dist/esm/next/components/Wizard/types.d.ts +37 -46
  67. package/dist/esm/next/components/Wizard/types.d.ts.map +1 -1
  68. package/dist/esm/next/components/Wizard/types.js +10 -2
  69. package/dist/esm/next/components/Wizard/types.js.map +1 -1
  70. package/dist/esm/next/components/Wizard/utils.d.ts +4 -5
  71. package/dist/esm/next/components/Wizard/utils.d.ts.map +1 -1
  72. package/dist/esm/next/components/Wizard/utils.js +20 -13
  73. package/dist/esm/next/components/Wizard/utils.js.map +1 -1
  74. package/dist/esm/next/components/index.d.ts +2 -1
  75. package/dist/esm/next/components/index.d.ts.map +1 -1
  76. package/dist/esm/next/components/index.js +2 -1
  77. package/dist/esm/next/components/index.js.map +1 -1
  78. package/dist/js/next/components/Select/Select.d.ts +28 -0
  79. package/dist/js/next/components/Select/Select.d.ts.map +1 -0
  80. package/dist/js/next/components/Select/Select.js +65 -0
  81. package/dist/js/next/components/Select/Select.js.map +1 -0
  82. package/dist/js/next/components/Select/SelectGroup.d.ts +12 -0
  83. package/dist/js/next/components/Select/SelectGroup.d.ts.map +1 -0
  84. package/dist/js/next/components/Select/SelectGroup.js +14 -0
  85. package/dist/js/next/components/Select/SelectGroup.js.map +1 -0
  86. package/dist/js/next/components/Select/SelectList.d.ts +10 -0
  87. package/dist/js/next/components/Select/SelectList.d.ts.map +1 -0
  88. package/dist/js/next/components/Select/SelectList.js +14 -0
  89. package/dist/js/next/components/Select/SelectList.js.map +1 -0
  90. package/dist/js/next/components/Select/SelectOption.d.ts +20 -0
  91. package/dist/js/next/components/Select/SelectOption.d.ts.map +1 -0
  92. package/dist/js/next/components/Select/SelectOption.js +14 -0
  93. package/dist/js/next/components/Select/SelectOption.js.map +1 -0
  94. package/dist/js/next/components/Select/index.d.ts +5 -0
  95. package/dist/js/next/components/Select/index.d.ts.map +1 -0
  96. package/dist/js/next/components/Select/index.js +8 -0
  97. package/dist/js/next/components/Select/index.js.map +1 -0
  98. package/dist/js/next/components/Wizard/Wizard.d.ts +9 -7
  99. package/dist/js/next/components/Wizard/Wizard.d.ts.map +1 -1
  100. package/dist/js/next/components/Wizard/Wizard.js +59 -45
  101. package/dist/js/next/components/Wizard/Wizard.js.map +1 -1
  102. package/dist/js/next/components/Wizard/WizardBody.d.ts +2 -2
  103. package/dist/js/next/components/Wizard/WizardBody.d.ts.map +1 -1
  104. package/dist/js/next/components/Wizard/WizardBody.js +1 -1
  105. package/dist/js/next/components/Wizard/WizardBody.js.map +1 -1
  106. package/dist/js/next/components/Wizard/WizardContext.d.ts +21 -20
  107. package/dist/js/next/components/Wizard/WizardContext.d.ts.map +1 -1
  108. package/dist/js/next/components/Wizard/WizardContext.js +61 -30
  109. package/dist/js/next/components/Wizard/WizardContext.js.map +1 -1
  110. package/dist/js/next/components/Wizard/WizardFooter.d.ts +20 -6
  111. package/dist/js/next/components/Wizard/WizardFooter.d.ts.map +1 -1
  112. package/dist/js/next/components/Wizard/WizardFooter.js +15 -6
  113. package/dist/js/next/components/Wizard/WizardFooter.js.map +1 -1
  114. package/dist/js/next/components/Wizard/WizardHeader.d.ts +1 -1
  115. package/dist/js/next/components/Wizard/WizardHeader.d.ts.map +1 -1
  116. package/dist/js/next/components/Wizard/WizardHeader.js +2 -2
  117. package/dist/js/next/components/Wizard/WizardHeader.js.map +1 -1
  118. package/dist/js/next/components/Wizard/WizardNav.d.ts +5 -5
  119. package/dist/js/next/components/Wizard/WizardNav.d.ts.map +1 -1
  120. package/dist/js/next/components/Wizard/WizardNav.js +3 -4
  121. package/dist/js/next/components/Wizard/WizardNav.js.map +1 -1
  122. package/dist/js/next/components/Wizard/WizardNavInternal.d.ts +14 -0
  123. package/dist/js/next/components/Wizard/WizardNavInternal.d.ts.map +1 -0
  124. package/dist/js/next/components/Wizard/WizardNavInternal.js +54 -0
  125. package/dist/js/next/components/Wizard/WizardNavInternal.js.map +1 -0
  126. package/dist/js/next/components/Wizard/WizardNavItem.d.ts +12 -8
  127. package/dist/js/next/components/Wizard/WizardNavItem.d.ts.map +1 -1
  128. package/dist/js/next/components/Wizard/WizardNavItem.js +23 -12
  129. package/dist/js/next/components/Wizard/WizardNavItem.js.map +1 -1
  130. package/dist/js/next/components/Wizard/WizardStep.d.ts +22 -6
  131. package/dist/js/next/components/Wizard/WizardStep.d.ts.map +1 -1
  132. package/dist/js/next/components/Wizard/WizardStep.js +20 -1
  133. package/dist/js/next/components/Wizard/WizardStep.js.map +1 -1
  134. package/dist/js/next/components/Wizard/WizardToggle.d.ts +9 -15
  135. package/dist/js/next/components/Wizard/WizardToggle.d.ts.map +1 -1
  136. package/dist/js/next/components/Wizard/WizardToggle.js +15 -51
  137. package/dist/js/next/components/Wizard/WizardToggle.js.map +1 -1
  138. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts +2 -2
  139. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -1
  140. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js +3 -3
  141. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js.map +1 -1
  142. package/dist/js/next/components/Wizard/types.d.ts +37 -46
  143. package/dist/js/next/components/Wizard/types.d.ts.map +1 -1
  144. package/dist/js/next/components/Wizard/types.js +12 -3
  145. package/dist/js/next/components/Wizard/types.js.map +1 -1
  146. package/dist/js/next/components/Wizard/utils.d.ts +4 -5
  147. package/dist/js/next/components/Wizard/utils.d.ts.map +1 -1
  148. package/dist/js/next/components/Wizard/utils.js +24 -15
  149. package/dist/js/next/components/Wizard/utils.js.map +1 -1
  150. package/dist/js/next/components/index.d.ts +2 -1
  151. package/dist/js/next/components/index.d.ts.map +1 -1
  152. package/dist/js/next/components/index.js +2 -1
  153. package/dist/js/next/components/index.js.map +1 -1
  154. package/dist/umd/react-core.min.css +940 -940
  155. package/package.json +2 -2
  156. package/src/next/components/Select/Select.tsx +129 -0
  157. package/src/next/components/Select/SelectGroup.tsx +24 -0
  158. package/src/next/components/Select/SelectList.tsx +21 -0
  159. package/src/next/components/Select/SelectOption.tsx +31 -0
  160. package/src/next/components/Select/examples/Select.md +37 -0
  161. package/src/next/components/Select/examples/SelectBasic.tsx +54 -0
  162. package/src/next/components/Select/examples/SelectCheckbox.tsx +67 -0
  163. package/src/next/components/Select/examples/SelectGrouped.tsx +63 -0
  164. package/src/next/components/Select/examples/SelectMultiTypeahead.tsx +195 -0
  165. package/src/next/components/Select/examples/SelectTypeahead.tsx +191 -0
  166. package/src/next/components/Select/index.ts +4 -0
  167. package/src/next/components/Wizard/Wizard.tsx +107 -64
  168. package/src/next/components/Wizard/WizardBody.tsx +4 -4
  169. package/src/next/components/Wizard/WizardContext.tsx +117 -65
  170. package/src/next/components/Wizard/WizardFooter.tsx +39 -17
  171. package/src/next/components/Wizard/WizardHeader.tsx +6 -3
  172. package/src/next/components/Wizard/WizardNav.tsx +9 -10
  173. package/src/next/components/Wizard/WizardNavInternal.tsx +137 -0
  174. package/src/next/components/Wizard/WizardNavItem.tsx +47 -20
  175. package/src/next/components/Wizard/WizardStep.tsx +52 -7
  176. package/src/next/components/Wizard/WizardToggle.tsx +24 -142
  177. package/src/next/components/Wizard/__tests__/Wizard.test.tsx +10 -13
  178. package/src/next/components/Wizard/__tests__/WizardBody.test.tsx +3 -3
  179. package/src/next/components/Wizard/__tests__/WizardFooter.test.tsx +11 -14
  180. package/src/next/components/Wizard/__tests__/WizardStep.test.tsx +100 -0
  181. package/src/next/components/Wizard/__tests__/WizardToggle.test.tsx +4 -4
  182. package/src/next/components/Wizard/examples/Wizard.md +40 -15
  183. package/src/next/components/Wizard/examples/WizardBasic.tsx +2 -2
  184. package/src/next/components/Wizard/examples/WizardCustomNav.tsx +4 -4
  185. package/src/next/components/Wizard/examples/WizardKitchenSink.tsx +126 -60
  186. package/src/next/components/Wizard/hooks/__tests__/useWizardFooter.test.tsx +23 -34
  187. package/src/next/components/Wizard/hooks/useWizardFooter.tsx +5 -5
  188. package/src/next/components/Wizard/types.tsx +59 -53
  189. package/src/next/components/Wizard/utils.ts +25 -16
  190. package/src/next/components/index.ts +2 -1
@@ -1,17 +1,16 @@
1
1
  import React from 'react';
2
2
 
3
- import { css } from '@patternfly/react-styles';
4
- import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
5
-
6
- import { DefaultWizardFooterProps, isCustomWizardFooter, WizardControlStep } from './types';
7
- import { getActiveStep } from './utils';
8
- import { WizardFooter } from './WizardFooter';
3
+ import { isCustomWizardFooter, isWizardParentStep, WizardControlStep, WizardFooterType } from './types';
4
+ import { getCurrentStep } from './utils';
5
+ import { WizardFooter, WizardFooterProps } from './WizardFooter';
9
6
 
10
7
  export interface WizardContextProps {
11
8
  /** List of steps */
12
9
  steps: WizardControlStep[];
13
- /** Active step */
14
- activeStep: WizardControlStep;
10
+ /** Current step */
11
+ currentStep: WizardControlStep;
12
+ /** Current step index */
13
+ currentStepIndex: number;
15
14
  /** Footer element */
16
15
  footer: React.ReactElement;
17
16
  /** Navigate to the next step */
@@ -27,38 +26,36 @@ export interface WizardContextProps {
27
26
  /** Navigate to step by index */
28
27
  goToStepByIndex: (index: number) => void;
29
28
  /** Update the footer with any react element */
30
- setFooter: (footer: DefaultWizardFooterProps | React.ReactElement) => void;
29
+ setFooter: (footer: React.ReactElement | Partial<WizardFooterProps>) => void;
30
+ /** Get step by ID */
31
+ getStep: (stepId: number | string) => WizardControlStep;
32
+ /** Set step by ID */
33
+ setStep: (step: Pick<WizardControlStep, 'id'> & Partial<WizardControlStep>) => void;
34
+ /** Toggle step visibility by ID */
35
+ toggleStep: (stepId: number | string, isHidden: boolean) => void;
31
36
  }
32
37
 
33
38
  export const WizardContext = React.createContext({} as WizardContextProps);
34
39
 
35
- interface WizardContextRenderProps {
36
- steps: WizardControlStep[];
37
- activeStep: WizardControlStep;
38
- footer: React.ReactElement;
39
- onNext(): void;
40
- onBack(): void;
41
- onClose(): void;
42
- }
43
-
44
40
  export interface WizardContextProviderProps {
45
41
  steps: WizardControlStep[];
46
42
  currentStepIndex: number;
47
- footer: DefaultWizardFooterProps | React.ReactElement;
48
- children: React.ReactElement | ((props: WizardContextRenderProps) => React.ReactElement);
49
- onNext(): void;
50
- onBack(): void;
43
+ footer: WizardFooterType;
44
+ isStepVisitRequired: boolean;
45
+ children: React.ReactElement;
46
+ onNext(steps: WizardControlStep[]): void;
47
+ onBack(steps: WizardControlStep[]): void;
51
48
  onClose(): void;
52
- goToStepById(id: number | string): void;
53
- goToStepByName(name: string): void;
54
- goToStepByIndex(index: number): void;
49
+ goToStepById(steps: WizardControlStep[], id: number | string): void;
50
+ goToStepByName(steps: WizardControlStep[], name: string): void;
51
+ goToStepByIndex(steps: WizardControlStep[], index: number): void;
55
52
  }
56
53
 
57
- // eslint-disable-next-line patternfly-react/no-anonymous-functions
58
54
  export const WizardContextProvider: React.FunctionComponent<WizardContextProviderProps> = ({
59
55
  steps: initialSteps,
60
56
  footer: initialFooter,
61
57
  currentStepIndex,
58
+ isStepVisitRequired,
62
59
  children,
63
60
  onNext,
64
61
  onBack,
@@ -68,61 +65,116 @@ export const WizardContextProvider: React.FunctionComponent<WizardContextProvide
68
65
  goToStepByIndex
69
66
  }) => {
70
67
  const [steps, setSteps] = React.useState(initialSteps);
71
- const [footer, setFooter] = React.useState(initialFooter);
72
- const activeStep = getActiveStep(steps, currentStepIndex);
73
-
74
- const wizardFooter = React.useMemo(
75
- () =>
76
- isCustomWizardFooter(footer) ? (
77
- <div className={css(styles.wizardFooter)}>{footer}</div>
78
- ) : (
79
- <WizardFooter
80
- activeStep={activeStep}
81
- onNext={onNext}
82
- onBack={onBack}
83
- onClose={onClose}
84
- disableBackButton={activeStep?.id === steps[0]?.id}
85
- {...footer}
86
- />
87
- ),
88
- [activeStep, footer, onBack, onClose, onNext, steps]
68
+ const [currentFooter, setCurrentFooter] = React.useState(
69
+ typeof initialFooter !== 'function' ? initialFooter : undefined
89
70
  );
71
+ const currentStep = getCurrentStep(steps, currentStepIndex);
72
+
73
+ const goToNextStep = React.useCallback(() => onNext(steps), [onNext, steps]);
74
+ const goToPrevStep = React.useCallback(() => onBack(steps), [onBack, steps]);
75
+
76
+ const footer = React.useMemo(() => {
77
+ const wizardFooter = currentFooter || initialFooter;
78
+
79
+ if (isCustomWizardFooter(wizardFooter)) {
80
+ const customFooter = wizardFooter;
81
+
82
+ return typeof customFooter === 'function'
83
+ ? customFooter(currentStep, goToNextStep, goToPrevStep, onClose)
84
+ : customFooter;
85
+ }
86
+
87
+ return (
88
+ <WizardFooter
89
+ currentStep={currentStep}
90
+ onNext={goToNextStep}
91
+ onBack={goToPrevStep}
92
+ onClose={onClose}
93
+ isBackDisabled={currentStep?.id === steps[0]?.id}
94
+ {...wizardFooter}
95
+ />
96
+ );
97
+ }, [currentFooter, initialFooter, currentStep, goToNextStep, goToPrevStep, onClose, steps]);
90
98
 
91
- // When the active step changes and the newly active step isn't visited, set the visited flag to true.
92
- React.useEffect(() => {
93
- if (activeStep && !activeStep?.visited) {
99
+ const getStep = React.useCallback((stepId: string | number) => steps.find(step => step.id === stepId), [steps]);
100
+
101
+ const setStep = React.useCallback(
102
+ (step: Pick<WizardControlStep, 'id'> & Partial<WizardControlStep>) =>
94
103
  setSteps(prevSteps =>
95
- prevSteps.map(step => {
96
- if (step.id === activeStep.id) {
97
- return { ...step, visited: true };
104
+ prevSteps.map(prevStep => {
105
+ if (prevStep.id === step.id) {
106
+ return { ...prevStep, ...step };
98
107
  }
99
108
 
100
- return step;
109
+ return prevStep;
101
110
  })
102
- );
103
- }
104
- }, [activeStep]);
111
+ ),
112
+ []
113
+ );
114
+
115
+ const toggleStep = React.useCallback(
116
+ (stepId: string | number, isHidden: boolean) =>
117
+ setSteps(prevSteps => {
118
+ let stepToHide: WizardControlStep;
119
+
120
+ return prevSteps.map(prevStep => {
121
+ if (prevStep.id === stepId) {
122
+ // Don't hide the currently active step or its parent (if a sub-step).
123
+ if (
124
+ isHidden &&
125
+ (currentStep.id === prevStep.id ||
126
+ (isWizardParentStep(prevStep) && prevStep.subStepIds.includes(currentStep.id)))
127
+ ) {
128
+ // eslint-disable-next-line no-console
129
+ console.error('Wizard: Unable to hide the current step or its parent.');
130
+ return prevStep;
131
+ }
132
+
133
+ stepToHide = { ...prevStep, isHidden };
134
+ return stepToHide;
135
+ }
136
+
137
+ // When isStepVisitRequired is enabled, if the step was previously hidden and not visited yet,
138
+ // when it is shown, all steps beyond it should be disabled to ensure it is visited.
139
+ if (
140
+ isStepVisitRequired &&
141
+ stepToHide?.isHidden === false &&
142
+ !stepToHide?.isVisited &&
143
+ prevSteps.indexOf(stepToHide) < prevSteps.indexOf(prevStep)
144
+ ) {
145
+ return { ...prevStep, isVisited: false };
146
+ }
147
+
148
+ return prevStep;
149
+ });
150
+ }),
151
+ [currentStep.id, isStepVisitRequired]
152
+ );
105
153
 
106
154
  return (
107
155
  <WizardContext.Provider
108
156
  value={{
109
157
  steps,
110
- activeStep,
111
- footer: wizardFooter,
112
- onNext,
113
- onBack,
158
+ currentStep,
159
+ currentStepIndex,
160
+ footer,
114
161
  onClose,
115
- goToStepById,
116
- goToStepByName,
117
- goToStepByIndex,
118
- setFooter
162
+ getStep,
163
+ setStep,
164
+ toggleStep,
165
+ setFooter: setCurrentFooter,
166
+ onNext: goToNextStep,
167
+ onBack: goToPrevStep,
168
+ goToStepById: React.useCallback(id => goToStepById(steps, id), [goToStepById, steps]),
169
+ goToStepByName: React.useCallback(name => goToStepByName(steps, name), [goToStepByName, steps]),
170
+ goToStepByIndex: React.useCallback(index => goToStepByIndex(steps, index), [goToStepByIndex, steps])
119
171
  }}
120
172
  >
121
- {typeof children === 'function'
122
- ? children({ activeStep, steps, footer: wizardFooter, onNext, onBack, onClose })
123
- : children}
173
+ {children}
124
174
  </WizardContext.Provider>
125
175
  );
126
176
  };
127
177
 
178
+ WizardContextProvider.displayName = 'WizardContextProvider';
179
+
128
180
  export const useWizardContext = () => React.useContext(WizardContext);
@@ -4,60 +4,82 @@ import { css } from '@patternfly/react-styles';
4
4
  import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
5
5
 
6
6
  import { Button, ButtonVariant } from '../../../components/Button';
7
- import { WizardControlStep, WizardNavStepFunction } from './types';
7
+ import { isCustomWizardFooter, WizardControlStep, WizardNavStepFunction } from './types';
8
8
 
9
9
  /**
10
10
  * Hosts the standard structure of a footer with ties to the active step so that text for buttons can vary from step to step.
11
11
  */
12
12
 
13
13
  export interface WizardFooterProps {
14
- /** The currently active WizardStep */
15
- activeStep: WizardControlStep;
14
+ /** The current step */
15
+ currentStep: WizardControlStep;
16
16
  /** Next button callback */
17
17
  onNext: () => WizardNavStepFunction | void;
18
18
  /** Back button callback */
19
19
  onBack: () => WizardNavStepFunction | void;
20
20
  /** Cancel link callback */
21
21
  onClose: () => void;
22
- /** Custom text for the Next button. The activeStep's nextButtonText takes precedence. */
22
+ /** Custom text for the Next button. The current step's nextButtonText takes precedence. */
23
23
  nextButtonText?: React.ReactNode;
24
24
  /** Custom text for the Back button */
25
25
  backButtonText?: React.ReactNode;
26
26
  /** Custom text for the Cancel link */
27
27
  cancelButtonText?: React.ReactNode;
28
- /** Optional flag to disable the first step's back button */
29
- disableBackButton?: boolean;
28
+ /** Flag to disable the next button */
29
+ isNextDisabled?: boolean;
30
+ /** Flag to disable the back button */
31
+ isBackDisabled?: boolean;
32
+ /** True to hide the Back button */
33
+ isBackHidden?: boolean;
34
+ /** True to hide the Cancel button */
35
+ isCancelHidden?: boolean;
30
36
  }
31
37
 
32
- export const WizardFooter = ({
38
+ interface WizardFooterWrapperProps {
39
+ children: React.ReactNode;
40
+ }
41
+
42
+ export const WizardFooterWrapper = ({ children }: WizardFooterWrapperProps) => (
43
+ <footer className={css(styles.wizardFooter)}>{children}</footer>
44
+ );
45
+
46
+ export const WizardFooter = ({ currentStep, ...internalProps }: WizardFooterProps) => {
47
+ const currentStepFooter = !isCustomWizardFooter(currentStep.footer) && currentStep.footer;
48
+ return <InternalWizardFooter {...internalProps} {...currentStepFooter} />;
49
+ };
50
+
51
+ const InternalWizardFooter = ({
33
52
  onNext,
34
53
  onBack,
35
54
  onClose,
36
- activeStep,
37
- disableBackButton,
55
+ isNextDisabled,
56
+ isBackDisabled,
57
+ isBackHidden,
58
+ isCancelHidden,
38
59
  nextButtonText = 'Next',
39
60
  backButtonText = 'Back',
40
61
  cancelButtonText = 'Cancel'
41
- }: WizardFooterProps) => (
42
- <footer className={css(styles.wizardFooter)}>
43
- <Button variant={ButtonVariant.primary} type="submit" onClick={onNext} isDisabled={activeStep?.disableNext}>
44
- {activeStep?.nextButtonText || nextButtonText}
62
+ }: Omit<WizardFooterProps, 'currentStep'>) => (
63
+ <WizardFooterWrapper>
64
+ <Button variant={ButtonVariant.primary} type="submit" onClick={onNext} isDisabled={isNextDisabled}>
65
+ {nextButtonText}
45
66
  </Button>
46
67
 
47
- {!activeStep?.hideBackButton && (
48
- <Button variant={ButtonVariant.secondary} onClick={onBack} isDisabled={disableBackButton}>
68
+ {!isBackHidden && (
69
+ <Button variant={ButtonVariant.secondary} onClick={onBack} isDisabled={isBackDisabled}>
49
70
  {backButtonText}
50
71
  </Button>
51
72
  )}
52
73
 
53
- {!activeStep?.hideCancelButton && (
74
+ {!isCancelHidden && (
54
75
  <div className={styles.wizardFooterCancel}>
55
76
  <Button variant={ButtonVariant.link} onClick={onClose}>
56
77
  {cancelButtonText}
57
78
  </Button>
58
79
  </div>
59
80
  )}
60
- </footer>
81
+ </WizardFooterWrapper>
61
82
  );
62
83
 
84
+ WizardFooterWrapper.displayName = 'WizardFooterWrapper';
63
85
  WizardFooter.displayName = 'WizardFooter';
@@ -15,7 +15,7 @@ export interface WizardHeaderProps {
15
15
  /** Component type of the description */
16
16
  descriptionComponent?: 'div' | 'p';
17
17
  /** Flag indicating whether the close button should be in the header */
18
- hideClose?: boolean;
18
+ isCloseHidden?: boolean;
19
19
  /** Aria-label applied to the X (Close) button */
20
20
  closeButtonAriaLabel?: string;
21
21
  /** id for the title */
@@ -28,21 +28,23 @@ export const WizardHeader: React.FunctionComponent<WizardHeaderProps> = ({
28
28
  onClose = () => undefined,
29
29
  title,
30
30
  description,
31
- hideClose,
31
+ isCloseHidden,
32
32
  closeButtonAriaLabel,
33
33
  titleId,
34
34
  descriptionComponent: Component = 'p',
35
35
  descriptionId
36
36
  }: WizardHeaderProps) => (
37
37
  <div className={css(styles.wizardHeader)}>
38
- {!hideClose && (
38
+ {!isCloseHidden && (
39
39
  <Button variant="plain" className={css(styles.wizardClose)} aria-label={closeButtonAriaLabel} onClick={onClose}>
40
40
  <TimesIcon aria-hidden="true" />
41
41
  </Button>
42
42
  )}
43
+
43
44
  <Title headingLevel="h2" size="3xl" className={css(styles.wizardTitle)} aria-label={title} id={titleId}>
44
45
  {title || <>&nbsp;</>}
45
46
  </Title>
47
+
46
48
  {description && (
47
49
  <Component className={css(styles.wizardDescription)} id={descriptionId}>
48
50
  {description}
@@ -50,4 +52,5 @@ export const WizardHeader: React.FunctionComponent<WizardHeaderProps> = ({
50
52
  )}
51
53
  </div>
52
54
  );
55
+
53
56
  WizardHeader.displayName = 'WizardHeader';
@@ -5,14 +5,14 @@ import { css } from '@patternfly/react-styles';
5
5
  export interface WizardNavProps {
6
6
  /** children should be WizardNavItem components */
7
7
  children?: any;
8
- /** Aria-label applied to the nav element */
8
+ /** Aria-label applied to the navigation element */
9
9
  'aria-label'?: string;
10
- /** Sets the aria-labelledby attribute on the nav element */
10
+ /** Sets the aria-labelledby attribute on the navigation element */
11
11
  'aria-labelledby'?: string;
12
- /** Whether the nav is expanded */
12
+ /** Whether the navigation is expanded */
13
13
  isExpanded?: boolean;
14
- /** True to return the inner list without the wrapping nav element */
15
- returnList?: boolean;
14
+ /** True to return the inner list without the wrapping navigation element */
15
+ isInnerList?: boolean;
16
16
  }
17
17
 
18
18
  export const WizardNav: React.FunctionComponent<WizardNavProps> = ({
@@ -20,12 +20,10 @@ export const WizardNav: React.FunctionComponent<WizardNavProps> = ({
20
20
  'aria-label': ariaLabel,
21
21
  'aria-labelledby': ariaLabelledBy,
22
22
  isExpanded = false,
23
- returnList = false
23
+ isInnerList = false
24
24
  }: WizardNavProps) => {
25
- const innerList = <ol className={css(styles.wizardNavList)}>{children}</ol>;
26
-
27
- if (returnList) {
28
- return innerList;
25
+ if (isInnerList) {
26
+ return <ol className={css(styles.wizardNavList)}>{children}</ol>;
29
27
  }
30
28
 
31
29
  return (
@@ -38,4 +36,5 @@ export const WizardNav: React.FunctionComponent<WizardNavProps> = ({
38
36
  </nav>
39
37
  );
40
38
  };
39
+
41
40
  WizardNav.displayName = 'WizardNav';
@@ -0,0 +1,137 @@
1
+ import React from 'react';
2
+
3
+ import { isCustomWizardNavItem, isWizardBasicStep, isWizardParentStep } from './types';
4
+ import { WizardProps } from './Wizard';
5
+ import { useWizardContext } from './WizardContext';
6
+ import { WizardNav, WizardNavProps } from './WizardNav';
7
+ import { WizardNavItem } from './WizardNavItem';
8
+
9
+ /**
10
+ * Hosts deafult wizard navigation logic by utilizing the wizard's context and WizardNav/WizardNavItem.
11
+ * This component is not exposed to consumers.
12
+ */
13
+
14
+ interface WizardNavInternalProps extends Pick<WizardProps, 'isStepVisitRequired'> {
15
+ nav: Partial<WizardNavProps>;
16
+ isNavExpanded: boolean;
17
+ }
18
+
19
+ export const WizardNavInternal = ({ nav, isStepVisitRequired, isNavExpanded }: WizardNavInternalProps) => {
20
+ const { currentStep, steps, goToStepByIndex } = useWizardContext();
21
+
22
+ const wizardNavProps: WizardNavProps = {
23
+ isExpanded: isNavExpanded,
24
+ 'aria-label': nav?.['aria-label'] || 'Wizard steps',
25
+ ...(nav?.['aria-labelledby'] && {
26
+ 'aria-labelledby': nav['aria-labelledby']
27
+ })
28
+ };
29
+
30
+ return (
31
+ <WizardNav {...wizardNavProps}>
32
+ {steps.map((step, index) => {
33
+ const stepIndex = index + 1;
34
+ const customStepNavItem = isCustomWizardNavItem(step.navItem) && (
35
+ <React.Fragment key={step.id}>
36
+ {typeof step.navItem === 'function'
37
+ ? step.navItem(step, currentStep, steps, goToStepByIndex)
38
+ : step.navItem}
39
+ </React.Fragment>
40
+ );
41
+
42
+ if (isWizardParentStep(step) && !step.isHidden) {
43
+ let firstSubStepIndex;
44
+ let hasActiveChild = false;
45
+
46
+ const subNavItems = step.subStepIds?.map((subStepId, subStepIndex) => {
47
+ const subStep = steps.find(step => step.id === subStepId);
48
+ const subStepOrderIndex = steps.indexOf(subStep) + 1;
49
+ const customSubStepNavItem = isCustomWizardNavItem(subStep.navItem) && (
50
+ <React.Fragment key={subStep.id}>
51
+ {typeof subStep.navItem === 'function'
52
+ ? subStep.navItem(subStep, currentStep, steps, goToStepByIndex)
53
+ : subStep.navItem}
54
+ </React.Fragment>
55
+ );
56
+
57
+ // Don't render the sub-step navigation item if the hidden property is enabled
58
+ if (subStep.isHidden) {
59
+ return;
60
+ }
61
+
62
+ // Store the first sub-step index so that when its parent is clicked, the first sub-step is focused
63
+ if (subStepIndex === 0) {
64
+ firstSubStepIndex = subStepOrderIndex;
65
+ }
66
+
67
+ // When a sub-step is active, use this flag to set the parent step as active (isCurrent)
68
+ if (currentStep?.id === subStep.id) {
69
+ hasActiveChild = true;
70
+ }
71
+
72
+ return (
73
+ customSubStepNavItem || (
74
+ <WizardNavItem
75
+ key={subStep.id}
76
+ id={subStep.id}
77
+ content={subStep.name}
78
+ isCurrent={currentStep?.id === subStep.id}
79
+ isDisabled={subStep.isDisabled || (isStepVisitRequired && !subStep.isVisited)}
80
+ isVisited={subStep.isVisited}
81
+ stepIndex={subStepOrderIndex}
82
+ onNavItemClick={goToStepByIndex}
83
+ status={subStep.status}
84
+ {...subStep.navItem}
85
+ />
86
+ )
87
+ );
88
+ });
89
+ const hasEnabledChildren = React.Children.toArray(subNavItems).some(
90
+ child => React.isValidElement(child) && !child.props.isDisabled
91
+ );
92
+
93
+ return (
94
+ customStepNavItem || (
95
+ <WizardNavItem
96
+ key={step.id}
97
+ id={step.id}
98
+ content={step.name}
99
+ isExpandable={true}
100
+ isCurrent={hasActiveChild}
101
+ isDisabled={!hasEnabledChildren}
102
+ isVisited={step.isVisited}
103
+ stepIndex={firstSubStepIndex}
104
+ onNavItemClick={goToStepByIndex}
105
+ status={step.status}
106
+ {...step.navItem}
107
+ >
108
+ <WizardNav {...wizardNavProps} isInnerList>
109
+ {subNavItems}
110
+ </WizardNav>
111
+ </WizardNavItem>
112
+ )
113
+ );
114
+ }
115
+
116
+ if (isWizardBasicStep(step) && !step.isHidden) {
117
+ return (
118
+ customStepNavItem || (
119
+ <WizardNavItem
120
+ key={step.id}
121
+ id={step.id}
122
+ content={step.name}
123
+ isCurrent={currentStep?.id === step.id}
124
+ isDisabled={step.isDisabled || (isStepVisitRequired && !step.isVisited)}
125
+ isVisited={step.isVisited}
126
+ stepIndex={stepIndex}
127
+ onNavItemClick={goToStepByIndex}
128
+ status={step.status}
129
+ {...step.navItem}
130
+ />
131
+ )
132
+ );
133
+ }
134
+ })}
135
+ </WizardNav>
136
+ );
137
+ };