@patternfly/react-core 4.248.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 (228) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/esm/components/Icon/Icon.d.ts +25 -0
  3. package/dist/esm/components/Icon/Icon.d.ts.map +1 -0
  4. package/dist/esm/components/Icon/Icon.js +14 -0
  5. package/dist/esm/components/Icon/Icon.js.map +1 -0
  6. package/dist/esm/components/Icon/index.d.ts +2 -0
  7. package/dist/esm/components/Icon/index.d.ts.map +1 -0
  8. package/dist/esm/components/Icon/index.js +2 -0
  9. package/dist/esm/components/Icon/index.js.map +1 -0
  10. package/dist/esm/components/index.d.ts +1 -0
  11. package/dist/esm/components/index.d.ts.map +1 -1
  12. package/dist/esm/components/index.js +1 -0
  13. package/dist/esm/components/index.js.map +1 -1
  14. package/dist/esm/next/components/Select/Select.d.ts +28 -0
  15. package/dist/esm/next/components/Select/Select.d.ts.map +1 -0
  16. package/dist/esm/next/components/Select/Select.js +62 -0
  17. package/dist/esm/next/components/Select/Select.js.map +1 -0
  18. package/dist/esm/next/components/Select/SelectGroup.d.ts +12 -0
  19. package/dist/esm/next/components/Select/SelectGroup.d.ts.map +1 -0
  20. package/dist/esm/next/components/Select/SelectGroup.js +10 -0
  21. package/dist/esm/next/components/Select/SelectGroup.js.map +1 -0
  22. package/dist/esm/next/components/Select/SelectList.d.ts +10 -0
  23. package/dist/esm/next/components/Select/SelectList.d.ts.map +1 -0
  24. package/dist/esm/next/components/Select/SelectList.js +10 -0
  25. package/dist/esm/next/components/Select/SelectList.js.map +1 -0
  26. package/dist/esm/next/components/Select/SelectOption.d.ts +20 -0
  27. package/dist/esm/next/components/Select/SelectOption.d.ts.map +1 -0
  28. package/dist/esm/next/components/Select/SelectOption.js +10 -0
  29. package/dist/esm/next/components/Select/SelectOption.js.map +1 -0
  30. package/dist/esm/next/components/Select/index.d.ts +5 -0
  31. package/dist/esm/next/components/Select/index.d.ts.map +1 -0
  32. package/dist/esm/next/components/Select/index.js +5 -0
  33. package/dist/esm/next/components/Select/index.js.map +1 -0
  34. package/dist/esm/next/components/Wizard/Wizard.d.ts +9 -7
  35. package/dist/esm/next/components/Wizard/Wizard.d.ts.map +1 -1
  36. package/dist/esm/next/components/Wizard/Wizard.js +62 -48
  37. package/dist/esm/next/components/Wizard/Wizard.js.map +1 -1
  38. package/dist/esm/next/components/Wizard/WizardBody.d.ts +2 -2
  39. package/dist/esm/next/components/Wizard/WizardBody.d.ts.map +1 -1
  40. package/dist/esm/next/components/Wizard/WizardBody.js +1 -1
  41. package/dist/esm/next/components/Wizard/WizardBody.js.map +1 -1
  42. package/dist/esm/next/components/Wizard/WizardContext.d.ts +21 -20
  43. package/dist/esm/next/components/Wizard/WizardContext.d.ts.map +1 -1
  44. package/dist/esm/next/components/Wizard/WizardContext.js +63 -32
  45. package/dist/esm/next/components/Wizard/WizardContext.js.map +1 -1
  46. package/dist/esm/next/components/Wizard/WizardFooter.d.ts +20 -6
  47. package/dist/esm/next/components/Wizard/WizardFooter.d.ts.map +1 -1
  48. package/dist/esm/next/components/Wizard/WizardFooter.js +13 -4
  49. package/dist/esm/next/components/Wizard/WizardFooter.js.map +1 -1
  50. package/dist/esm/next/components/Wizard/WizardHeader.d.ts +1 -1
  51. package/dist/esm/next/components/Wizard/WizardHeader.d.ts.map +1 -1
  52. package/dist/esm/next/components/Wizard/WizardHeader.js +2 -2
  53. package/dist/esm/next/components/Wizard/WizardHeader.js.map +1 -1
  54. package/dist/esm/next/components/Wizard/WizardNav.d.ts +5 -5
  55. package/dist/esm/next/components/Wizard/WizardNav.d.ts.map +1 -1
  56. package/dist/esm/next/components/Wizard/WizardNav.js +3 -4
  57. package/dist/esm/next/components/Wizard/WizardNav.js.map +1 -1
  58. package/dist/esm/next/components/Wizard/WizardNavInternal.d.ts +14 -0
  59. package/dist/esm/next/components/Wizard/WizardNavInternal.d.ts.map +1 -0
  60. package/dist/esm/next/components/Wizard/WizardNavInternal.js +49 -0
  61. package/dist/esm/next/components/Wizard/WizardNavInternal.js.map +1 -0
  62. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts +12 -8
  63. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts.map +1 -1
  64. package/dist/esm/next/components/Wizard/WizardNavItem.js +23 -12
  65. package/dist/esm/next/components/Wizard/WizardNavItem.js.map +1 -1
  66. package/dist/esm/next/components/Wizard/WizardStep.d.ts +22 -6
  67. package/dist/esm/next/components/Wizard/WizardStep.d.ts.map +1 -1
  68. package/dist/esm/next/components/Wizard/WizardStep.js +20 -1
  69. package/dist/esm/next/components/Wizard/WizardStep.js.map +1 -1
  70. package/dist/esm/next/components/Wizard/WizardToggle.d.ts +9 -15
  71. package/dist/esm/next/components/Wizard/WizardToggle.d.ts.map +1 -1
  72. package/dist/esm/next/components/Wizard/WizardToggle.js +15 -50
  73. package/dist/esm/next/components/Wizard/WizardToggle.js.map +1 -1
  74. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts +2 -2
  75. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -1
  76. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js +3 -3
  77. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js.map +1 -1
  78. package/dist/esm/next/components/Wizard/types.d.ts +37 -46
  79. package/dist/esm/next/components/Wizard/types.d.ts.map +1 -1
  80. package/dist/esm/next/components/Wizard/types.js +10 -2
  81. package/dist/esm/next/components/Wizard/types.js.map +1 -1
  82. package/dist/esm/next/components/Wizard/utils.d.ts +4 -5
  83. package/dist/esm/next/components/Wizard/utils.d.ts.map +1 -1
  84. package/dist/esm/next/components/Wizard/utils.js +20 -13
  85. package/dist/esm/next/components/Wizard/utils.js.map +1 -1
  86. package/dist/esm/next/components/index.d.ts +2 -1
  87. package/dist/esm/next/components/index.d.ts.map +1 -1
  88. package/dist/esm/next/components/index.js +2 -1
  89. package/dist/esm/next/components/index.js.map +1 -1
  90. package/dist/js/components/Icon/Icon.d.ts +25 -0
  91. package/dist/js/components/Icon/Icon.d.ts.map +1 -0
  92. package/dist/js/components/Icon/Icon.js +18 -0
  93. package/dist/js/components/Icon/Icon.js.map +1 -0
  94. package/dist/js/components/Icon/index.d.ts +2 -0
  95. package/dist/js/components/Icon/index.d.ts.map +1 -0
  96. package/dist/js/components/Icon/index.js +5 -0
  97. package/dist/js/components/Icon/index.js.map +1 -0
  98. package/dist/js/components/index.d.ts +1 -0
  99. package/dist/js/components/index.d.ts.map +1 -1
  100. package/dist/js/components/index.js +1 -0
  101. package/dist/js/components/index.js.map +1 -1
  102. package/dist/js/next/components/Select/Select.d.ts +28 -0
  103. package/dist/js/next/components/Select/Select.d.ts.map +1 -0
  104. package/dist/js/next/components/Select/Select.js +65 -0
  105. package/dist/js/next/components/Select/Select.js.map +1 -0
  106. package/dist/js/next/components/Select/SelectGroup.d.ts +12 -0
  107. package/dist/js/next/components/Select/SelectGroup.d.ts.map +1 -0
  108. package/dist/js/next/components/Select/SelectGroup.js +14 -0
  109. package/dist/js/next/components/Select/SelectGroup.js.map +1 -0
  110. package/dist/js/next/components/Select/SelectList.d.ts +10 -0
  111. package/dist/js/next/components/Select/SelectList.d.ts.map +1 -0
  112. package/dist/js/next/components/Select/SelectList.js +14 -0
  113. package/dist/js/next/components/Select/SelectList.js.map +1 -0
  114. package/dist/js/next/components/Select/SelectOption.d.ts +20 -0
  115. package/dist/js/next/components/Select/SelectOption.d.ts.map +1 -0
  116. package/dist/js/next/components/Select/SelectOption.js +14 -0
  117. package/dist/js/next/components/Select/SelectOption.js.map +1 -0
  118. package/dist/js/next/components/Select/index.d.ts +5 -0
  119. package/dist/js/next/components/Select/index.d.ts.map +1 -0
  120. package/dist/js/next/components/Select/index.js +8 -0
  121. package/dist/js/next/components/Select/index.js.map +1 -0
  122. package/dist/js/next/components/Wizard/Wizard.d.ts +9 -7
  123. package/dist/js/next/components/Wizard/Wizard.d.ts.map +1 -1
  124. package/dist/js/next/components/Wizard/Wizard.js +59 -45
  125. package/dist/js/next/components/Wizard/Wizard.js.map +1 -1
  126. package/dist/js/next/components/Wizard/WizardBody.d.ts +2 -2
  127. package/dist/js/next/components/Wizard/WizardBody.d.ts.map +1 -1
  128. package/dist/js/next/components/Wizard/WizardBody.js +1 -1
  129. package/dist/js/next/components/Wizard/WizardBody.js.map +1 -1
  130. package/dist/js/next/components/Wizard/WizardContext.d.ts +21 -20
  131. package/dist/js/next/components/Wizard/WizardContext.d.ts.map +1 -1
  132. package/dist/js/next/components/Wizard/WizardContext.js +61 -30
  133. package/dist/js/next/components/Wizard/WizardContext.js.map +1 -1
  134. package/dist/js/next/components/Wizard/WizardFooter.d.ts +20 -6
  135. package/dist/js/next/components/Wizard/WizardFooter.d.ts.map +1 -1
  136. package/dist/js/next/components/Wizard/WizardFooter.js +15 -6
  137. package/dist/js/next/components/Wizard/WizardFooter.js.map +1 -1
  138. package/dist/js/next/components/Wizard/WizardHeader.d.ts +1 -1
  139. package/dist/js/next/components/Wizard/WizardHeader.d.ts.map +1 -1
  140. package/dist/js/next/components/Wizard/WizardHeader.js +2 -2
  141. package/dist/js/next/components/Wizard/WizardHeader.js.map +1 -1
  142. package/dist/js/next/components/Wizard/WizardNav.d.ts +5 -5
  143. package/dist/js/next/components/Wizard/WizardNav.d.ts.map +1 -1
  144. package/dist/js/next/components/Wizard/WizardNav.js +3 -4
  145. package/dist/js/next/components/Wizard/WizardNav.js.map +1 -1
  146. package/dist/js/next/components/Wizard/WizardNavInternal.d.ts +14 -0
  147. package/dist/js/next/components/Wizard/WizardNavInternal.d.ts.map +1 -0
  148. package/dist/js/next/components/Wizard/WizardNavInternal.js +54 -0
  149. package/dist/js/next/components/Wizard/WizardNavInternal.js.map +1 -0
  150. package/dist/js/next/components/Wizard/WizardNavItem.d.ts +12 -8
  151. package/dist/js/next/components/Wizard/WizardNavItem.d.ts.map +1 -1
  152. package/dist/js/next/components/Wizard/WizardNavItem.js +23 -12
  153. package/dist/js/next/components/Wizard/WizardNavItem.js.map +1 -1
  154. package/dist/js/next/components/Wizard/WizardStep.d.ts +22 -6
  155. package/dist/js/next/components/Wizard/WizardStep.d.ts.map +1 -1
  156. package/dist/js/next/components/Wizard/WizardStep.js +20 -1
  157. package/dist/js/next/components/Wizard/WizardStep.js.map +1 -1
  158. package/dist/js/next/components/Wizard/WizardToggle.d.ts +9 -15
  159. package/dist/js/next/components/Wizard/WizardToggle.d.ts.map +1 -1
  160. package/dist/js/next/components/Wizard/WizardToggle.js +15 -51
  161. package/dist/js/next/components/Wizard/WizardToggle.js.map +1 -1
  162. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts +2 -2
  163. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -1
  164. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js +3 -3
  165. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js.map +1 -1
  166. package/dist/js/next/components/Wizard/types.d.ts +37 -46
  167. package/dist/js/next/components/Wizard/types.d.ts.map +1 -1
  168. package/dist/js/next/components/Wizard/types.js +12 -3
  169. package/dist/js/next/components/Wizard/types.js.map +1 -1
  170. package/dist/js/next/components/Wizard/utils.d.ts +4 -5
  171. package/dist/js/next/components/Wizard/utils.d.ts.map +1 -1
  172. package/dist/js/next/components/Wizard/utils.js +24 -15
  173. package/dist/js/next/components/Wizard/utils.js.map +1 -1
  174. package/dist/js/next/components/index.d.ts +2 -1
  175. package/dist/js/next/components/index.d.ts.map +1 -1
  176. package/dist/js/next/components/index.js +2 -1
  177. package/dist/js/next/components/index.js.map +1 -1
  178. package/dist/umd/react-core.min.css +337 -200
  179. package/dist/umd/react-core.min.js +1 -1
  180. package/package.json +2 -2
  181. package/src/components/Icon/Icon.tsx +66 -0
  182. package/src/components/Icon/__tests__/Icon.test.tsx +196 -0
  183. package/src/components/Icon/__tests__/__snapshots__/Icon.test.tsx.snap +82 -0
  184. package/src/components/Icon/examples/Icon.md +61 -0
  185. package/src/components/Icon/examples/IconBasic.tsx +23 -0
  186. package/src/components/Icon/examples/IconContentSizes.tsx +20 -0
  187. package/src/components/Icon/examples/IconCustomProgress.tsx +24 -0
  188. package/src/components/Icon/examples/IconInline.tsx +62 -0
  189. package/src/components/Icon/examples/IconProgress.tsx +24 -0
  190. package/src/components/Icon/examples/IconSizes.tsx +20 -0
  191. package/src/components/Icon/examples/IconStatus.tsx +27 -0
  192. package/src/components/Icon/index.ts +1 -0
  193. package/src/components/index.ts +1 -0
  194. package/src/next/components/Select/Select.tsx +129 -0
  195. package/src/next/components/Select/SelectGroup.tsx +24 -0
  196. package/src/next/components/Select/SelectList.tsx +21 -0
  197. package/src/next/components/Select/SelectOption.tsx +31 -0
  198. package/src/next/components/Select/examples/Select.md +37 -0
  199. package/src/next/components/Select/examples/SelectBasic.tsx +54 -0
  200. package/src/next/components/Select/examples/SelectCheckbox.tsx +67 -0
  201. package/src/next/components/Select/examples/SelectGrouped.tsx +63 -0
  202. package/src/next/components/Select/examples/SelectMultiTypeahead.tsx +195 -0
  203. package/src/next/components/Select/examples/SelectTypeahead.tsx +191 -0
  204. package/src/next/components/Select/index.ts +4 -0
  205. package/src/next/components/Wizard/Wizard.tsx +107 -64
  206. package/src/next/components/Wizard/WizardBody.tsx +4 -4
  207. package/src/next/components/Wizard/WizardContext.tsx +117 -65
  208. package/src/next/components/Wizard/WizardFooter.tsx +39 -17
  209. package/src/next/components/Wizard/WizardHeader.tsx +6 -3
  210. package/src/next/components/Wizard/WizardNav.tsx +9 -10
  211. package/src/next/components/Wizard/WizardNavInternal.tsx +137 -0
  212. package/src/next/components/Wizard/WizardNavItem.tsx +47 -20
  213. package/src/next/components/Wizard/WizardStep.tsx +52 -7
  214. package/src/next/components/Wizard/WizardToggle.tsx +24 -142
  215. package/src/next/components/Wizard/__tests__/Wizard.test.tsx +10 -13
  216. package/src/next/components/Wizard/__tests__/WizardBody.test.tsx +3 -3
  217. package/src/next/components/Wizard/__tests__/WizardFooter.test.tsx +11 -14
  218. package/src/next/components/Wizard/__tests__/WizardStep.test.tsx +100 -0
  219. package/src/next/components/Wizard/__tests__/WizardToggle.test.tsx +4 -4
  220. package/src/next/components/Wizard/examples/Wizard.md +40 -15
  221. package/src/next/components/Wizard/examples/WizardBasic.tsx +2 -2
  222. package/src/next/components/Wizard/examples/WizardCustomNav.tsx +4 -4
  223. package/src/next/components/Wizard/examples/WizardKitchenSink.tsx +126 -60
  224. package/src/next/components/Wizard/hooks/__tests__/useWizardFooter.test.tsx +23 -34
  225. package/src/next/components/Wizard/hooks/useWizardFooter.tsx +5 -5
  226. package/src/next/components/Wizard/types.tsx +59 -53
  227. package/src/next/components/Wizard/utils.ts +25 -16
  228. package/src/next/components/index.ts +2 -1
@@ -1,29 +1,37 @@
1
1
  import * as React from 'react';
2
+
2
3
  import { css } from '@patternfly/react-styles';
3
4
  import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
4
5
  import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
6
+ import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
7
+
8
+ import { WizardNavItemStatus } from './types';
5
9
 
6
10
  export interface WizardNavItemProps {
7
11
  /** Can nest a WizardNav component for substeps */
8
12
  children?: React.ReactNode;
9
- /** The content to display in the nav item */
13
+ /** The content to display in the navigation item */
10
14
  content?: React.ReactNode;
11
- /** Whether the nav item is the currently active item */
15
+ /** Whether the navigation item is the currently active item */
12
16
  isCurrent?: boolean;
13
- /** Whether the nav item is disabled */
17
+ /** Whether the navigation item is disabled */
14
18
  isDisabled?: boolean;
15
- /** The step passed into the onNavItemClick callback */
16
- step: number;
17
- /** Callback for when the nav item is clicked */
18
- onNavItemClick?: (step: number) => any;
19
+ /** Whether the navigation item has been visited */
20
+ isVisited?: boolean;
21
+ /** The step index passed into the onNavItemClick callback */
22
+ stepIndex: number;
23
+ /** Callback for when the navigation item is clicked */
24
+ onNavItemClick?: (stepIndex: number) => any;
19
25
  /** Component used to render WizardNavItem */
20
26
  navItemComponent?: 'button' | 'a';
21
27
  /** An optional url to use for when using an anchor component */
22
28
  href?: string;
23
29
  /** Flag indicating that this NavItem has child steps and is expandable */
24
30
  isExpandable?: boolean;
25
- /** The id for the nav item */
31
+ /** The id for the navigation item */
26
32
  id?: string | number;
33
+ /** Used to determine the icon displayed next to content. Default has no icon. */
34
+ status?: 'default' | 'error';
27
35
  }
28
36
 
29
37
  export const WizardNavItem: React.FunctionComponent<WizardNavItemProps> = ({
@@ -31,16 +39,17 @@ export const WizardNavItem: React.FunctionComponent<WizardNavItemProps> = ({
31
39
  content = '',
32
40
  isCurrent = false,
33
41
  isDisabled = false,
34
- step,
42
+ isVisited = false,
43
+ stepIndex,
35
44
  onNavItemClick = () => undefined,
36
45
  navItemComponent = 'button',
37
46
  href = null,
38
47
  isExpandable = false,
39
48
  id,
49
+ status = 'default',
40
50
  ...rest
41
51
  }: WizardNavItemProps) => {
42
52
  const NavItemComponent = navItemComponent;
43
-
44
53
  const [isExpanded, setIsExpanded] = React.useState(false);
45
54
 
46
55
  React.useEffect(() => {
@@ -52,14 +61,22 @@ export const WizardNavItem: React.FunctionComponent<WizardNavItemProps> = ({
52
61
  console.error('WizardNavItem: When using an anchor, please provide an href');
53
62
  }
54
63
 
55
- const btnProps = {
56
- disabled: isDisabled
57
- };
64
+ const ariaLabel = React.useMemo(() => {
65
+ if (status === WizardNavItemStatus.Error || (isVisited && !isCurrent)) {
66
+ let label = content.toString();
67
+
68
+ if (status === WizardNavItemStatus.Error) {
69
+ label += `, ${status}`;
70
+ }
71
+
72
+ // No need to signify step is visited if current
73
+ if (isVisited && !isCurrent) {
74
+ label += ', visited';
75
+ }
58
76
 
59
- const linkProps = {
60
- tabIndex: isDisabled ? -1 : undefined,
61
- href
62
- };
77
+ return label;
78
+ }
79
+ }, [content, isCurrent, isVisited, status]);
63
80
 
64
81
  return (
65
82
  <li
@@ -71,11 +88,11 @@ export const WizardNavItem: React.FunctionComponent<WizardNavItemProps> = ({
71
88
  >
72
89
  <NavItemComponent
73
90
  {...rest}
74
- {...(navItemComponent === 'a' ? { ...linkProps } : { ...btnProps })}
91
+ {...(navItemComponent === 'a' ? { tabIndex: isDisabled ? -1 : undefined, href } : { disabled: isDisabled })}
75
92
  {...(id && { id: id.toString() })}
76
93
  onClick={e => {
77
94
  e.preventDefault();
78
- isExpandable ? setIsExpanded(!isExpanded || isCurrent) : onNavItemClick(step);
95
+ isExpandable ? setIsExpanded(!isExpanded || isCurrent) : onNavItemClick(stepIndex);
79
96
  }}
80
97
  className={css(
81
98
  styles.wizardNavLink,
@@ -85,6 +102,7 @@ export const WizardNavItem: React.FunctionComponent<WizardNavItemProps> = ({
85
102
  aria-disabled={isDisabled ? true : null}
86
103
  aria-current={isCurrent && !children ? 'step' : false}
87
104
  {...(isExpandable && { 'aria-expanded': isExpanded })}
105
+ {...(ariaLabel && { 'aria-label': ariaLabel })}
88
106
  >
89
107
  {isExpandable ? (
90
108
  <>
@@ -96,11 +114,20 @@ export const WizardNavItem: React.FunctionComponent<WizardNavItemProps> = ({
96
114
  </span>
97
115
  </>
98
116
  ) : (
99
- content
117
+ <>
118
+ {content}
119
+ {/* TODO, patternfly/patternfly#5142 */}
120
+ {status === WizardNavItemStatus.Error && (
121
+ <span style={{ marginLeft: 'var(--pf-global--spacer--sm)' }}>
122
+ <ExclamationCircleIcon color="var(--pf-global--danger-color--100)" />
123
+ </span>
124
+ )}
125
+ </>
100
126
  )}
101
127
  </NavItemComponent>
102
128
  {children}
103
129
  </li>
104
130
  );
105
131
  };
132
+
106
133
  WizardNavItem.displayName = 'WizardNavItem';
@@ -1,23 +1,68 @@
1
1
  import React from 'react';
2
2
 
3
- import { WizardBasicStep } from './types';
3
+ import { WizardNavItemType } from './types';
4
4
  import { WizardBody, WizardBodyProps } from './WizardBody';
5
+ import { useWizardFooter } from './hooks/useWizardFooter';
6
+ import { useWizardContext } from './WizardContext';
7
+ import { WizardFooterProps } from './WizardFooter';
5
8
 
6
9
  /**
7
- * Used as a passthrough of step properties for Wizard and all supporting child components.
8
- * Also acts as a wrapper for content, with an optional inclusion of WizardBody.
10
+ * The primary child component for Wizard. Step props are used for the list of steps managed in context.
9
11
  */
10
12
 
11
- export interface WizardStepProps extends Omit<WizardBasicStep, 'visited'> {
13
+ export interface WizardStepProps {
14
+ /** Name of the step's navigation item */
15
+ name: React.ReactNode;
16
+ /** Unique identifier */
17
+ id: string | number;
12
18
  /** Optional for when the step is used as a parent to sub-steps */
13
19
  children?: React.ReactNode;
14
20
  /** Props for WizardBody that wraps content by default. Can be set to null for exclusion of WizardBody. */
15
- body?: Omit<WizardBodyProps, 'children'> | null;
21
+ body?: Omit<Omit<WizardBodyProps, 'children'>, 'children'> | null;
16
22
  /** Optional list of sub-steps */
17
23
  steps?: React.ReactElement<WizardStepProps>[];
24
+ /** Flag to disable the step's navigation item */
25
+ isDisabled?: boolean;
26
+ /** Flag to represent whether the step has been visited (navigated to) */
27
+ isVisited?: boolean;
28
+ /** Flag to determine whether the step is hidden */
29
+ isHidden?: boolean;
30
+ /** Replaces the step's navigation item or its properties. */
31
+ navItem?: WizardNavItemType;
32
+ /** Replaces the step's footer. The step's footer takes precedance over the wizard's footer. */
33
+ footer?: React.ReactElement | Partial<WizardFooterProps>;
34
+ /** Used to determine icon next to the step's navigation item */
35
+ status?: 'default' | 'error';
18
36
  }
19
37
 
20
- export const WizardStep = ({ body, children }: WizardStepProps) =>
21
- body || body === undefined ? <WizardBody {...body}>{children}</WizardBody> : <>{children}</>;
38
+ export const WizardStep = ({ children, body, id, footer, isHidden, isDisabled, navItem, status }: WizardStepProps) => {
39
+ const { currentStep, setStep, toggleStep } = useWizardContext();
40
+
41
+ useWizardFooter(footer, id);
42
+
43
+ // Update the controlled step when a change is detected with select properties
44
+ React.useEffect(() => {
45
+ if (currentStep?.id === id && (isDisabled || navItem || status || !currentStep?.isVisited)) {
46
+ setStep({
47
+ id,
48
+ ...(isDisabled && { isDisabled }),
49
+ ...(navItem && { navItem }),
50
+ ...(status && { status }),
51
+ // When the current step is active and isn't visited, set as isVisited
52
+ ...(!currentStep?.isVisited && { isVisited: true })
53
+ });
54
+ }
55
+ }, [id, setStep, currentStep?.id, isDisabled, navItem, status, currentStep?.isVisited]);
56
+
57
+ // Toggle visibility when the isHidden flag updates.
58
+ // Wizard's hasUnmountedSteps prop must be set to false for visibility changes to take effect.
59
+ React.useEffect(() => {
60
+ if (isHidden !== undefined) {
61
+ toggleStep(id, isHidden);
62
+ }
63
+ }, [toggleStep, id, isHidden]);
64
+
65
+ return body || body === undefined ? <WizardBody {...body}>{children}</WizardBody> : <>{children}</>;
66
+ };
22
67
 
23
68
  WizardStep.displayName = 'WizardStep';
@@ -6,52 +6,48 @@ import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-i
6
6
  import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
7
7
 
8
8
  import { KeyTypes } from '../../../helpers/constants';
9
- import { WizardNav, WizardNavItem, WizardNavProps } from '../Wizard';
10
- import {
11
- WizardControlStep,
12
- isWizardBasicStep,
13
- isWizardParentStep,
14
- isWizardSubStep,
15
- isCustomWizardNav,
16
- DefaultWizardNavProps,
17
- CustomWizardNavFunction
18
- } from './types';
19
- import { useWizardContext } from './WizardContext';
9
+ import { WizardNavProps } from '../Wizard';
10
+ import { WizardControlStep, isWizardSubStep } from './types';
20
11
 
21
12
  /**
22
- * Used to toggle between step content, including the body and footer. This is also where the nav and its expandability is controlled.
13
+ * Used to toggle between step content, including the body and footer. This is also where the navigation and its expandability is controlled.
23
14
  */
24
15
 
25
16
  export interface WizardToggleProps {
26
17
  /** List of steps and/or sub-steps */
27
18
  steps: WizardControlStep[];
28
- /** The currently active WizardStep */
29
- activeStep: WizardControlStep;
19
+ /** The current step */
20
+ currentStep: WizardControlStep;
30
21
  /** Wizard footer */
31
22
  footer: React.ReactElement;
32
- /** Wizard nav */
23
+ /** Wizard navigation */
33
24
  nav: React.ReactElement<WizardNavProps>;
34
25
  /** The expandable dropdown button's aria-label */
35
26
  'aria-label'?: string;
36
27
  /** Flag to unmount inactive steps instead of hiding. Defaults to true */
37
- unmountInactiveSteps?: boolean;
38
- /** Flag to determine whether the dropdown nav is expanded */
28
+ hasUnmountedSteps?: boolean;
29
+ /** Flag to determine whether the dropdown navigation is expanded */
39
30
  isNavExpanded?: boolean;
40
- /** Callback to expand or collapse the dropdown nav */
31
+ /** Callback to expand or collapse the dropdown navigation */
41
32
  toggleNavExpanded?: () => void;
42
33
  }
43
34
 
44
35
  export const WizardToggle = ({
45
36
  steps,
46
- activeStep,
37
+ currentStep,
47
38
  footer,
48
39
  nav,
49
40
  isNavExpanded,
50
41
  toggleNavExpanded,
51
- unmountInactiveSteps = true,
42
+ hasUnmountedSteps = true,
52
43
  'aria-label': ariaLabel = 'Wizard toggle'
53
44
  }: WizardToggleProps) => {
54
- const isActiveSubStep = isWizardSubStep(activeStep);
45
+ const isActiveSubStep = isWizardSubStep(currentStep);
46
+ const nonSubSteps = steps.filter(step => !isWizardSubStep(step));
47
+ const wizardToggleIndex =
48
+ nonSubSteps.indexOf(
49
+ isWizardSubStep(currentStep) ? steps.find(step => step.id === currentStep.parentId) : currentStep
50
+ ) + 1;
55
51
 
56
52
  const handleKeyClicks = React.useCallback(
57
53
  (event: KeyboardEvent): void => {
@@ -62,7 +58,7 @@ export const WizardToggle = ({
62
58
  [isNavExpanded, toggleNavExpanded]
63
59
  );
64
60
 
65
- // Open/close collapsable nav on keydown event
61
+ // Open/close collapsable navigation on keydown event
66
62
  React.useEffect(() => {
67
63
  const target = typeof document !== 'undefined' ? document.body : null;
68
64
  target?.addEventListener('keydown', handleKeyClicks, false);
@@ -72,11 +68,11 @@ export const WizardToggle = ({
72
68
  };
73
69
  }, [handleKeyClicks]);
74
70
 
75
- // Only render the active step when unmountInactiveSteps is true
76
- const bodyContent = unmountInactiveSteps
77
- ? activeStep?.component
71
+ // Only render the current step when hasUnmountedSteps is true
72
+ const bodyContent = hasUnmountedSteps
73
+ ? currentStep?.component
78
74
  : steps.map(step => {
79
- if (activeStep?.name === step.name) {
75
+ if (currentStep?.name === step.name) {
80
76
  return step.component;
81
77
  }
82
78
 
@@ -97,10 +93,10 @@ export const WizardToggle = ({
97
93
  >
98
94
  <span className={css(styles.wizardToggleList)}>
99
95
  <span className={css(styles.wizardToggleListItem)}>
100
- {activeStep?.name}
96
+ <span className={css(styles.wizardToggleNum)}>{wizardToggleIndex}</span> {currentStep?.name}
101
97
  {isActiveSubStep && <AngleRightIcon className={css(styles.wizardToggleSeparator)} aria-hidden="true" />}
102
98
  </span>
103
- {isActiveSubStep && <span className={css(styles.wizardToggleListItem)}>{activeStep?.name}</span>}
99
+ {isActiveSubStep && <span className={css(styles.wizardToggleListItem)}>{currentStep?.name}</span>}
104
100
  </span>
105
101
 
106
102
  <span className={css(styles.wizardToggleIcon)}>
@@ -119,118 +115,4 @@ export const WizardToggle = ({
119
115
  );
120
116
  };
121
117
 
122
- interface WizardToggleInternalProps extends Pick<WizardToggleProps, 'unmountInactiveSteps'> {
123
- /** Custom WizardNav or callback used to create a default WizardNav */
124
- nav?: DefaultWizardNavProps | CustomWizardNavFunction;
125
- }
126
-
127
- export const WizardToggleInternal = ({ nav, unmountInactiveSteps }: WizardToggleInternalProps) => {
128
- const [isNavExpanded, setIsNavExpanded] = React.useState(false);
129
- const { activeStep, steps, footer, goToStepByIndex } = useWizardContext();
130
-
131
- const wizardNav = React.useMemo(() => {
132
- if (isCustomWizardNav(nav)) {
133
- return nav(isNavExpanded, steps, activeStep, goToStepByIndex);
134
- }
135
-
136
- const navProps = {
137
- isExpanded: isNavExpanded,
138
- 'aria-label': nav?.ariaLabel || 'Wizard nav',
139
- ...(nav?.ariaLabelledBy && { 'aria-labelledby': nav?.ariaLabelledBy })
140
- };
141
-
142
- return (
143
- <WizardNav {...navProps}>
144
- {steps.map((step, index) => {
145
- const stepIndex = index + 1;
146
- const stepNavItem = step.navItem && <React.Fragment key={step.id}>{step.navItem}</React.Fragment>;
147
-
148
- if (isWizardParentStep(step)) {
149
- let firstSubStepIndex;
150
- let hasActiveChild = false;
151
-
152
- const subNavItems = step.subStepIds?.map((subStepId, index) => {
153
- const subStep = steps.find(step => step.id === subStepId);
154
- const subStepIndex = steps.indexOf(subStep) + 1;
155
-
156
- if (index === 0) {
157
- firstSubStepIndex = subStepIndex;
158
- }
159
-
160
- if (activeStep?.id === subStep.id) {
161
- hasActiveChild = true;
162
- }
163
-
164
- return subStep.navItem ? (
165
- <React.Fragment key={subStep.id}>{subStep.navItem}</React.Fragment>
166
- ) : (
167
- <WizardNavItem
168
- key={subStep.id}
169
- id={subStep.id}
170
- content={subStep.name}
171
- isCurrent={activeStep?.id === subStep.id}
172
- isDisabled={subStep.isDisabled || (nav?.forceStepVisit && !subStep.visited)}
173
- step={subStepIndex}
174
- onNavItemClick={goToStepByIndex}
175
- />
176
- );
177
- });
178
-
179
- const hasEnabledChildren = React.Children.toArray(subNavItems).some(
180
- child => React.isValidElement(child) && !child.props.isDisabled
181
- );
182
-
183
- return (
184
- stepNavItem || (
185
- <WizardNavItem
186
- key={step.id}
187
- id={step.id}
188
- content={step.name}
189
- isExpandable={nav?.isExpandable}
190
- isCurrent={hasActiveChild}
191
- isDisabled={!hasEnabledChildren}
192
- step={firstSubStepIndex}
193
- onNavItemClick={goToStepByIndex}
194
- >
195
- <WizardNav {...navProps} returnList>
196
- {subNavItems}
197
- </WizardNav>
198
- </WizardNavItem>
199
- )
200
- );
201
- }
202
-
203
- if (isWizardBasicStep(step)) {
204
- return (
205
- stepNavItem || (
206
- <WizardNavItem
207
- key={step.id}
208
- id={step.id}
209
- content={step.name}
210
- isCurrent={activeStep?.id === step.id}
211
- isDisabled={step.isDisabled || (nav?.forceStepVisit && !step.visited)}
212
- step={stepIndex}
213
- onNavItemClick={goToStepByIndex}
214
- />
215
- )
216
- );
217
- }
218
- })}
219
- </WizardNav>
220
- );
221
- }, [activeStep, goToStepByIndex, isNavExpanded, nav, steps]);
222
-
223
- return (
224
- <WizardToggle
225
- nav={wizardNav}
226
- footer={footer}
227
- steps={steps}
228
- activeStep={activeStep}
229
- isNavExpanded={isNavExpanded}
230
- toggleNavExpanded={() => setIsNavExpanded(prevIsExpanded => !prevIsExpanded)}
231
- unmountInactiveSteps={unmountInactiveSteps}
232
- />
233
- );
234
- };
235
-
236
118
  WizardToggle.displayName = 'WizardToggle';
@@ -3,8 +3,7 @@ import React from 'react';
3
3
  import { render, screen } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
 
6
- import { DefaultWizardNavProps, DefaultWizardFooterProps } from '../types';
7
- import { Wizard, WizardStep } from '../';
6
+ import { Wizard, WizardFooterProps, WizardStep, WizardNavProps } from '../';
8
7
 
9
8
  test('renders step when child is of type WizardStep', () => {
10
9
  render(
@@ -57,7 +56,7 @@ test('renders default footer without specifying the footer prop', () => {
57
56
  });
58
57
 
59
58
  test('renders default footer with custom props', () => {
60
- const footer: DefaultWizardFooterProps = {
59
+ const footer: Partial<WizardFooterProps> = {
61
60
  nextButtonText: <>Proceed with caution</>,
62
61
  backButtonText: 'Turn back!',
63
62
  cancelButtonText: 'Leave now!'
@@ -96,15 +95,13 @@ test('renders default nav without specifying the nav prop', () => {
96
95
  });
97
96
 
98
97
  test('renders default nav with custom props', () => {
99
- const nav: DefaultWizardNavProps = {
100
- isExpandable: true,
101
- ariaLabel: 'Some nav label',
102
- ariaLabelledBy: 'wizard-id',
103
- forceStepVisit: true
98
+ const nav: Partial<WizardNavProps> = {
99
+ 'aria-label': 'Some nav label',
100
+ 'aria-labelledby': 'wizard-id'
104
101
  };
105
102
 
106
103
  render(
107
- <Wizard id="wizard-id" nav={nav}>
104
+ <Wizard id="wizard-id" nav={nav} isStepVisitRequired>
108
105
  <WizardStep id="step-1" name="Test step 1" steps={[<WizardStep id="nested-step" name="Nested step" />]} />
109
106
  <WizardStep id="step-2" name="Test step 2" />
110
107
  </Wizard>
@@ -119,7 +116,7 @@ test('renders default nav with custom props', () => {
119
116
  });
120
117
 
121
118
  test('renders custom nav', () => {
122
- const customNav = <nav>Some custom nav</nav>;
119
+ const customNav = () => <nav>Some custom nav</nav>;
123
120
 
124
121
  render(
125
122
  <Wizard nav={customNav}>
@@ -130,7 +127,7 @@ test('renders custom nav', () => {
130
127
  expect(screen.getByRole('navigation')).toBeVisible();
131
128
  });
132
129
 
133
- test('starts at the first step as the active one by default', () => {
130
+ test('starts with the first step as the current one by default', () => {
134
131
  render(
135
132
  <Wizard>
136
133
  <WizardStep id="step-1" name="Test step 1">
@@ -301,11 +298,11 @@ test('unmounts inactive steps by default', async () => {
301
298
  expect(screen.getByText('Step 2 content')).toBeVisible();
302
299
  });
303
300
 
304
- test('keeps inactive steps mounted when unmountInactiveSteps is enabled', async () => {
301
+ test('keeps inactive steps mounted when hasUnmountedSteps is enabled', async () => {
305
302
  const user = userEvent.setup();
306
303
 
307
304
  render(
308
- <Wizard unmountInactiveSteps={false}>
305
+ <Wizard hasUnmountedSteps={false}>
309
306
  <WizardStep id="step-1" name="Test step 1">
310
307
  Step 1 content
311
308
  </WizardStep>
@@ -30,12 +30,12 @@ test('has aria-labelledby when one is specified', () => {
30
30
  expect(container.firstElementChild).toHaveAttribute('aria-labelledby', 'some-id');
31
31
  });
32
32
 
33
- test('wrapper element is of type div when wrapperElement is not specified', () => {
33
+ test('wrapper element is of type div when component is not specified', () => {
34
34
  const { container } = render(<WizardBody aria-label="Wizard body">content</WizardBody>);
35
35
  expect(container.firstElementChild?.tagName).toEqual('DIV');
36
36
  });
37
37
 
38
- test('renders with custom wrapperElement', () => {
39
- const { container } = render(<WizardBody wrapperElement="main">content</WizardBody>);
38
+ test('renders with custom component', () => {
39
+ const { container } = render(<WizardBody component="main">content</WizardBody>);
40
40
  expect(container.firstElementChild?.tagName).toEqual('MAIN');
41
41
  });
@@ -3,10 +3,10 @@ import React from 'react';
3
3
  import { render, screen } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
 
6
- import { WizardFooter } from '../WizardFooter';
6
+ import { WizardFooter, WizardFooterProps } from '../WizardFooter';
7
7
 
8
- const defaultProps = {
9
- activeStep: { name: 'Step name', id: 'some-id' },
8
+ const defaultProps: WizardFooterProps = {
9
+ currentStep: { name: 'Step name', id: 'some-id' },
10
10
  onNext: jest.fn(),
11
11
  onBack: jest.fn(),
12
12
  onClose: jest.fn()
@@ -65,27 +65,24 @@ test('can have custom button names', () => {
65
65
  expect(screen.getByRole('button', { name: 'Get out!' })).toBeVisible();
66
66
  });
67
67
 
68
- test('has disabled back button when disableBackButton is enabled', () => {
69
- render(<WizardFooter {...defaultProps} disableBackButton />);
68
+ test('has disabled back button when isBackDisabled is enabled', () => {
69
+ render(<WizardFooter {...defaultProps} isBackDisabled />);
70
70
  expect(screen.getByRole('button', { name: 'Back' })).toHaveAttribute('disabled');
71
71
  });
72
72
 
73
- test('has no back button when activeStep has hideBackButton enabled', () => {
74
- render(<WizardFooter {...defaultProps} activeStep={{ ...defaultProps.activeStep, hideBackButton: true }} />);
73
+ test('uses currentStep footer properties when specified', () => {
74
+ render(
75
+ <WizardFooter {...defaultProps} currentStep={{ ...defaultProps.currentStep, footer: { isBackHidden: true } }} />
76
+ );
75
77
  expect(screen.queryByRole('button', { name: 'Back' })).toBeNull();
76
78
  });
77
79
 
78
- test('has no cancel button when activeStep has hideCancelButton enabled', () => {
79
- render(<WizardFooter {...defaultProps} activeStep={{ ...defaultProps.activeStep, hideCancelButton: true }} />);
80
- expect(screen.queryByRole('button', { name: 'Cancel' })).toBeNull();
81
- });
82
-
83
- test(`uses activeStep's nextButtonText when specified instead of nextButtonText of WizardFooter`, () => {
80
+ test(`currentStep footer properties take precendence over WizardFooter's`, () => {
84
81
  render(
85
82
  <WizardFooter
86
83
  {...defaultProps}
87
84
  nextButtonText="Footer next"
88
- activeStep={{ ...defaultProps.activeStep, nextButtonText: 'Active step next' }}
85
+ currentStep={{ ...defaultProps.currentStep, footer: { nextButtonText: 'Active step next' } }}
89
86
  />
90
87
  );
91
88
  expect(screen.queryByRole('button', { name: 'Footer next' })).toBeNull();