@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
@@ -0,0 +1,191 @@
1
+ import React from 'react';
2
+ import { Select, SelectOption, SelectList, SelectOptionProps } from '@patternfly/react-core/next';
3
+ import {
4
+ MenuToggle,
5
+ MenuToggleElement,
6
+ TextInputGroup,
7
+ TextInputGroupMain,
8
+ TextInputGroupUtilities,
9
+ Button
10
+ } from '@patternfly/react-core';
11
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
12
+
13
+ const initialSelectOptions: SelectOptionProps[] = [
14
+ { itemId: 'Alabama', children: 'Alabama' },
15
+ { itemId: 'Florida', children: 'Florida' },
16
+ { itemId: 'New Jersey', children: 'New Jersey' },
17
+ { itemId: 'New Mexico', children: 'New Mexico' },
18
+ { itemId: 'New York', children: 'New York' },
19
+ { itemId: 'North Carolina', children: 'North Carolina' }
20
+ ];
21
+
22
+ export const SelectBasic: React.FunctionComponent = () => {
23
+ const [isOpen, setIsOpen] = React.useState(false);
24
+ const [selected, setSelected] = React.useState<string>('');
25
+ const [inputValue, setInputValue] = React.useState<string>('');
26
+ const [filterValue, setFilterValue] = React.useState<string>('');
27
+ const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
28
+ const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
29
+
30
+ const menuRef = React.useRef<HTMLDivElement>(null);
31
+ const textInputRef = React.useRef<HTMLInputElement>();
32
+
33
+ React.useEffect(() => {
34
+ let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
35
+
36
+ // Filter menu items based on the text input value when one exists
37
+ if (filterValue) {
38
+ newSelectOptions = initialSelectOptions.filter(menuItem =>
39
+ String(menuItem.children)
40
+ .toLowerCase()
41
+ .includes(filterValue.toLowerCase())
42
+ );
43
+
44
+ // When no options are found after filtering, display 'No results found'
45
+ if (!newSelectOptions.length) {
46
+ newSelectOptions = [{ isDisabled: true, children: 'No results found' }];
47
+ }
48
+ }
49
+
50
+ setSelectOptions(newSelectOptions);
51
+ }, [filterValue]);
52
+
53
+ const onToggleClick = () => {
54
+ setIsOpen(!isOpen);
55
+ };
56
+
57
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
58
+ // eslint-disable-next-line no-console
59
+ console.log('selected', itemId);
60
+
61
+ if (itemId) {
62
+ setInputValue(itemId as string);
63
+ setFilterValue(itemId as string);
64
+ setSelected(itemId as string);
65
+ }
66
+ setIsOpen(false);
67
+ setFocusedItemIndex(null);
68
+ };
69
+
70
+ const onTextInputChange = (value: string) => {
71
+ setInputValue(value);
72
+ setFilterValue(value);
73
+ };
74
+
75
+ const handleMenuArrowKeys = (key: string) => {
76
+ let indexToFocus;
77
+
78
+ if (isOpen) {
79
+ if (key === 'ArrowUp') {
80
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
81
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
82
+ indexToFocus = selectOptions.length - 1;
83
+ } else {
84
+ indexToFocus = focusedItemIndex - 1;
85
+ }
86
+ }
87
+
88
+ if (key === 'ArrowDown') {
89
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
90
+ if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
91
+ indexToFocus = 0;
92
+ } else {
93
+ indexToFocus = focusedItemIndex + 1;
94
+ }
95
+ }
96
+
97
+ setFocusedItemIndex(indexToFocus);
98
+ }
99
+ };
100
+
101
+ const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
102
+ const enabledMenuItems = selectOptions.filter(menuItem => !menuItem.isDisabled);
103
+ const [firstMenuItem] = enabledMenuItems;
104
+ const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
105
+
106
+ switch (event.key) {
107
+ // Select the first available option
108
+ case 'Enter':
109
+ if (isOpen) {
110
+ setInputValue(String(focusedItem.children));
111
+ setSelected(String(focusedItem.children));
112
+ }
113
+
114
+ setIsOpen(prevIsOpen => !prevIsOpen);
115
+ setFocusedItemIndex(null);
116
+
117
+ break;
118
+ case 'Tab':
119
+ case 'Escape':
120
+ setIsOpen(false);
121
+ break;
122
+ case 'ArrowUp':
123
+ case 'ArrowDown':
124
+ handleMenuArrowKeys(event.key);
125
+ break;
126
+ default:
127
+ !isOpen && setIsOpen(true);
128
+ }
129
+ };
130
+
131
+ const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
132
+ <MenuToggle ref={toggleRef} variant="typeahead" onClick={onToggleClick} isExpanded={isOpen} isFullWidth>
133
+ <TextInputGroup isPlain>
134
+ <TextInputGroupMain
135
+ value={inputValue}
136
+ onClick={onToggleClick}
137
+ onChange={onTextInputChange}
138
+ onKeyDown={onInputKeyDown}
139
+ id="typeahead-select-input"
140
+ autoComplete="off"
141
+ innerRef={textInputRef}
142
+ placeholder="Select a state"
143
+ />
144
+
145
+ <TextInputGroupUtilities>
146
+ {!!inputValue && (
147
+ <Button
148
+ variant="plain"
149
+ onClick={() => {
150
+ setSelected('');
151
+ setInputValue('');
152
+ setFilterValue('');
153
+ }}
154
+ aria-label="Clear input value"
155
+ >
156
+ <TimesIcon aria-hidden />
157
+ </Button>
158
+ )}
159
+ </TextInputGroupUtilities>
160
+ </TextInputGroup>
161
+ </MenuToggle>
162
+ );
163
+
164
+ return (
165
+ <Select
166
+ id="typeahead-select"
167
+ ref={menuRef}
168
+ isOpen={isOpen}
169
+ selected={selected}
170
+ onSelect={onSelect}
171
+ onOpenChange={() => {
172
+ setIsOpen(false);
173
+ setFilterValue('');
174
+ }}
175
+ toggle={toggle}
176
+ >
177
+ <SelectList>
178
+ {selectOptions.map((option, index) => (
179
+ <SelectOption
180
+ key={option.itemId || option.children}
181
+ isFocused={focusedItemIndex === index}
182
+ className={option.className}
183
+ onClick={() => setSelected(option.itemId)}
184
+ {...option}
185
+ ref={null}
186
+ />
187
+ ))}
188
+ </SelectList>
189
+ </Select>
190
+ );
191
+ };
@@ -0,0 +1,4 @@
1
+ export * from './Select';
2
+ export * from './SelectGroup';
3
+ export * from './SelectList';
4
+ export * from './SelectOption';
@@ -1,19 +1,22 @@
1
1
  import React from 'react';
2
+ import findLastIndex from 'lodash/findLastIndex';
2
3
 
3
4
  import { css } from '@patternfly/react-styles';
4
5
  import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
5
6
 
6
7
  import {
7
- DefaultWizardFooterProps,
8
- DefaultWizardNavProps,
9
8
  isWizardParentStep,
10
9
  WizardNavStepFunction,
11
- CustomWizardNavFunction
10
+ WizardControlStep,
11
+ isCustomWizardNav,
12
+ WizardFooterType,
13
+ WizardNavType
12
14
  } from './types';
13
15
  import { buildSteps, normalizeNavStep } from './utils';
14
- import { WizardContextProvider } from './WizardContext';
16
+ import { useWizardContext, WizardContextProvider } from './WizardContext';
15
17
  import { WizardStepProps } from './WizardStep';
16
- import { WizardToggleInternal } from './WizardToggle';
18
+ import { WizardToggle } from './WizardToggle';
19
+ import { WizardNavInternal } from './WizardNavInternal';
17
20
 
18
21
  /**
19
22
  * Wrapper for all steps and hosts state, including navigation helpers, within context.
@@ -26,9 +29,9 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
26
29
  /** Wizard header */
27
30
  header?: React.ReactNode;
28
31
  /** Wizard footer */
29
- footer?: DefaultWizardFooterProps | React.ReactElement;
30
- /** Wizard nav */
31
- nav?: DefaultWizardNavProps | CustomWizardNavFunction;
32
+ footer?: WizardFooterType;
33
+ /** Wizard navigation */
34
+ nav?: WizardNavType;
32
35
  /** The initial index the wizard is to start on (1 or higher). Defaults to 1. */
33
36
  startIndex?: number;
34
37
  /** Additional classes spread to the wizard */
@@ -37,9 +40,11 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
37
40
  width?: number | string;
38
41
  /** Custom height of the wizard */
39
42
  height?: number | string;
43
+ /** Disables navigation items that haven't been visited. Defaults to false */
44
+ isStepVisitRequired?: boolean;
40
45
  /** Flag to unmount inactive steps instead of hiding. Defaults to true */
41
- unmountInactiveSteps?: boolean;
42
- /** Callback function when a step in the nav is clicked */
46
+ hasUnmountedSteps?: boolean;
47
+ /** Callback function when a step in the navigation is clicked */
43
48
  onNavByIndex?: WizardNavStepFunction;
44
49
  /** Callback function after next button is clicked */
45
50
  onNext?: WizardNavStepFunction;
@@ -52,7 +57,6 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
52
57
  }
53
58
 
54
59
  export const Wizard = ({
55
- startIndex = 1,
56
60
  children,
57
61
  footer,
58
62
  height,
@@ -60,7 +64,9 @@ export const Wizard = ({
60
64
  className,
61
65
  header,
62
66
  nav,
63
- unmountInactiveSteps,
67
+ startIndex = 1,
68
+ isStepVisitRequired = false,
69
+ hasUnmountedSteps = true,
64
70
  onNavByIndex,
65
71
  onNext,
66
72
  onBack,
@@ -69,83 +75,91 @@ export const Wizard = ({
69
75
  ...wrapperProps
70
76
  }: WizardProps) => {
71
77
  const [currentStepIndex, setCurrentStepIndex] = React.useState(startIndex);
72
- const steps = buildSteps(children);
78
+ const initialSteps = buildSteps(children);
73
79
 
74
- const goToStepByIndex = (index: number) => {
75
- const lastStepIndex = steps.length;
80
+ const goToNextStep = (steps: WizardControlStep[] = initialSteps) => {
81
+ const newStepIndex =
82
+ steps.findIndex((step, index) => index + 1 > currentStepIndex && !step.isHidden && !isWizardParentStep(step)) + 1;
76
83
 
77
- if (index < 1) {
78
- index = 1;
79
- } else if (index > lastStepIndex) {
80
- index = lastStepIndex;
84
+ if (currentStepIndex >= steps.length || !newStepIndex) {
85
+ return onSave ? onSave() : onClose?.();
81
86
  }
82
87
 
83
- const currStep = steps[index - 1];
88
+ const currStep = isWizardParentStep(steps[currentStepIndex])
89
+ ? steps[currentStepIndex + 1]
90
+ : steps[currentStepIndex];
84
91
  const prevStep = steps[currentStepIndex - 1];
85
- setCurrentStepIndex(index);
86
92
 
87
- return onNavByIndex?.(normalizeNavStep(currStep), normalizeNavStep(prevStep));
93
+ setCurrentStepIndex(newStepIndex);
94
+
95
+ return onNext?.(normalizeNavStep(currStep, steps), normalizeNavStep(prevStep, steps));
88
96
  };
89
97
 
90
- const goToNextStep = () => {
91
- // Save when on the last step, otherwise close
92
- if (currentStepIndex >= steps.length) {
93
- if (onSave) {
94
- return onSave();
95
- }
98
+ const goToPrevStep = (steps: WizardControlStep[] = initialSteps) => {
99
+ const newStepIndex =
100
+ findLastIndex(
101
+ steps,
102
+ (step: WizardControlStep, index: number) =>
103
+ index + 1 < currentStepIndex && !step.isHidden && !isWizardParentStep(step)
104
+ ) + 1;
105
+ const currStep = isWizardParentStep(steps[currentStepIndex - 2])
106
+ ? steps[currentStepIndex - 3]
107
+ : steps[currentStepIndex - 2];
108
+ const prevStep = steps[currentStepIndex - 1];
96
109
 
97
- return onClose?.();
98
- }
110
+ setCurrentStepIndex(newStepIndex);
99
111
 
100
- let currStep = steps[currentStepIndex];
101
- let newStepIndex = currentStepIndex + 1;
102
- const prevStep = steps[currentStepIndex - 1];
112
+ return onBack?.(normalizeNavStep(currStep, steps), normalizeNavStep(prevStep, steps));
113
+ };
114
+
115
+ const goToStepByIndex = (steps: WizardControlStep[] = initialSteps, index: number) => {
116
+ const lastStepIndex = steps.length + 1;
103
117
 
104
- // Skip parent step and focus on the first sub-step if they exist
105
- if (isWizardParentStep(currStep)) {
106
- newStepIndex += 1;
107
- currStep = steps[currentStepIndex + 1];
118
+ // Handle index when out of bounds or hidden
119
+ if (index < 1) {
120
+ index = 1;
121
+ } else if (index > lastStepIndex) {
122
+ index = lastStepIndex;
123
+ } else if (steps[index - 1].isHidden) {
124
+ // eslint-disable-next-line no-console
125
+ console.error('Wizard: Unable to navigate to hidden step.');
108
126
  }
109
127
 
110
- setCurrentStepIndex(newStepIndex);
111
- return onNext?.(normalizeNavStep(currStep), normalizeNavStep(prevStep));
128
+ const currStep = steps[index - 1];
129
+ const prevStep = steps[currentStepIndex - 1];
130
+ setCurrentStepIndex(index);
131
+
132
+ return onNavByIndex?.(normalizeNavStep(currStep, steps), normalizeNavStep(prevStep, steps));
112
133
  };
113
134
 
114
- const goToPrevStep = () => {
115
- if (steps.length < currentStepIndex) {
116
- // Previous step was removed, just update the currentStep state
117
- setCurrentStepIndex(steps.length);
135
+ const goToStepById = (steps: WizardControlStep[] = initialSteps, id: number | string) => {
136
+ const stepIndex = steps.findIndex(step => step.id === id) + 1;
137
+
138
+ if (stepIndex > 0 && stepIndex < steps.length + 1 && !steps[stepIndex].isHidden) {
139
+ setCurrentStepIndex(stepIndex);
118
140
  } else {
119
- let currStep = steps[currentStepIndex - 2];
120
- let newStepIndex = currentStepIndex - 1;
121
- const prevStep = steps[currentStepIndex - 1];
122
-
123
- // // Skip parent step and focus on the step prior
124
- if (isWizardParentStep(currStep)) {
125
- newStepIndex -= 1;
126
- currStep = steps[currentStepIndex - 3];
127
- }
128
-
129
- setCurrentStepIndex(newStepIndex);
130
- return onBack?.(normalizeNavStep(currStep), normalizeNavStep(prevStep));
141
+ // eslint-disable-next-line no-console
142
+ console.error(`Wizard: Unable to navigate to step with id: ${id}.`);
131
143
  }
132
144
  };
133
145
 
134
- const goToStepById = (id: number | string) => {
135
- const stepIndex = steps.findIndex(step => step.id === id) + 1;
136
- stepIndex > 0 && setCurrentStepIndex(stepIndex);
137
- };
146
+ const goToStepByName = (steps: WizardControlStep[] = initialSteps, name: string) => {
147
+ const stepIndex = initialSteps.findIndex(step => step.name === name) + 1;
138
148
 
139
- const goToStepByName = (name: string) => {
140
- const stepIndex = steps.findIndex(step => step.name === name) + 1;
141
- stepIndex > 0 && setCurrentStepIndex(stepIndex);
149
+ if (stepIndex > 0 && stepIndex < steps.length + 1 && !steps[stepIndex].isHidden) {
150
+ setCurrentStepIndex(stepIndex);
151
+ } else {
152
+ // eslint-disable-next-line no-console
153
+ console.error(`Wizard: Unable to navigate to step with name: ${name}.`);
154
+ }
142
155
  };
143
156
 
144
157
  return (
145
158
  <WizardContextProvider
146
- steps={steps}
159
+ steps={initialSteps}
147
160
  currentStepIndex={currentStepIndex}
148
161
  footer={footer}
162
+ isStepVisitRequired={isStepVisitRequired}
149
163
  onNext={goToNextStep}
150
164
  onBack={goToPrevStep}
151
165
  onClose={onClose}
@@ -162,10 +176,39 @@ export const Wizard = ({
162
176
  {...wrapperProps}
163
177
  >
164
178
  {header}
165
- <WizardToggleInternal nav={nav} unmountInactiveSteps={unmountInactiveSteps} />
179
+ <WizardInternal nav={nav} hasUnmountedSteps={hasUnmountedSteps} isStepVisitRequired={isStepVisitRequired} />
166
180
  </div>
167
181
  </WizardContextProvider>
168
182
  );
169
183
  };
170
184
 
185
+ const WizardInternal = ({
186
+ nav,
187
+ hasUnmountedSteps,
188
+ isStepVisitRequired
189
+ }: Pick<WizardProps, 'nav' | 'hasUnmountedSteps' | 'isStepVisitRequired'>) => {
190
+ const { currentStep, steps, footer, goToStepByIndex } = useWizardContext();
191
+ const [isNavExpanded, setIsNavExpanded] = React.useState(false);
192
+
193
+ const wizardNav = React.useMemo(() => {
194
+ if (isCustomWizardNav(nav)) {
195
+ return typeof nav === 'function' ? nav(isNavExpanded, steps, currentStep, goToStepByIndex) : nav;
196
+ }
197
+
198
+ return <WizardNavInternal nav={nav} isNavExpanded={isNavExpanded} isStepVisitRequired={isStepVisitRequired} />;
199
+ }, [currentStep, isStepVisitRequired, goToStepByIndex, isNavExpanded, nav, steps]);
200
+
201
+ return (
202
+ <WizardToggle
203
+ nav={wizardNav}
204
+ footer={footer}
205
+ steps={steps}
206
+ currentStep={currentStep}
207
+ isNavExpanded={isNavExpanded}
208
+ toggleNavExpanded={() => setIsNavExpanded(prevIsExpanded => !prevIsExpanded)}
209
+ hasUnmountedSteps={hasUnmountedSteps}
210
+ />
211
+ );
212
+ };
213
+
171
214
  Wizard.displayName = 'Wizard';
@@ -16,7 +16,7 @@ export interface WizardBodyProps {
16
16
  /** Sets the aria-labelledby attribute for the wrapper element */
17
17
  'aria-labelledby'?: string;
18
18
  /** Component used as the wrapping content container */
19
- wrapperElement?: React.ElementType;
19
+ component?: React.ElementType;
20
20
  }
21
21
 
22
22
  export const WizardBody = ({
@@ -24,11 +24,11 @@ export const WizardBody = ({
24
24
  hasNoBodyPadding = false,
25
25
  'aria-label': ariaLabel,
26
26
  'aria-labelledby': ariaLabelledBy,
27
- wrapperElement: Wrapper = 'div'
27
+ component: WrapperComponent = 'div'
28
28
  }: WizardBodyProps) => (
29
- <Wrapper aria-label={ariaLabel} aria-labelledby={ariaLabelledBy} className={css(styles.wizardMain)}>
29
+ <WrapperComponent aria-label={ariaLabel} aria-labelledby={ariaLabelledBy} className={css(styles.wizardMain)}>
30
30
  <div className={css(styles.wizardMainBody, hasNoBodyPadding && styles.modifiers.noPadding)}>{children}</div>
31
- </Wrapper>
31
+ </WrapperComponent>
32
32
  );
33
33
 
34
34
  WizardBody.displayName = 'WizardBody';