@patternfly/react-core 4.234.2 → 4.235.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 (191) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/esm/components/LoginPage/LoginFooterItem.d.ts +3 -3
  3. package/dist/esm/components/LoginPage/LoginForm.d.ts +18 -18
  4. package/dist/esm/components/LoginPage/LoginHeader.d.ts +1 -1
  5. package/dist/esm/components/LoginPage/LoginMainBody.d.ts +2 -2
  6. package/dist/esm/components/LoginPage/LoginMainFooter.d.ts +5 -5
  7. package/dist/esm/components/LoginPage/LoginMainFooterBandItem.d.ts +2 -2
  8. package/dist/esm/components/LoginPage/LoginMainFooterLinksItem.d.ts +5 -5
  9. package/dist/esm/components/LoginPage/LoginMainHeader.d.ts +6 -4
  10. package/dist/esm/components/LoginPage/LoginMainHeader.d.ts.map +1 -1
  11. package/dist/esm/components/LoginPage/LoginMainHeader.js +2 -1
  12. package/dist/esm/components/LoginPage/LoginMainHeader.js.map +1 -1
  13. package/dist/esm/components/LoginPage/LoginPage.d.ts +16 -14
  14. package/dist/esm/components/LoginPage/LoginPage.d.ts.map +1 -1
  15. package/dist/esm/components/LoginPage/LoginPage.js +2 -2
  16. package/dist/esm/components/LoginPage/LoginPage.js.map +1 -1
  17. package/dist/esm/next/components/Wizard/Wizard.d.ts +40 -0
  18. package/dist/esm/next/components/Wizard/Wizard.d.ts.map +1 -0
  19. package/dist/esm/next/components/Wizard/Wizard.js +86 -0
  20. package/dist/esm/next/components/Wizard/Wizard.js.map +1 -0
  21. package/dist/esm/next/components/Wizard/WizardBody.d.ts +20 -0
  22. package/dist/esm/next/components/Wizard/WizardBody.d.ts.map +1 -0
  23. package/dist/esm/next/components/Wizard/WizardBody.js +7 -0
  24. package/dist/esm/next/components/Wizard/WizardBody.js.map +1 -0
  25. package/dist/esm/next/components/Wizard/WizardContext.d.ts +49 -0
  26. package/dist/esm/next/components/Wizard/WizardContext.d.ts.map +1 -0
  27. package/dist/esm/next/components/Wizard/WizardContext.js +34 -0
  28. package/dist/esm/next/components/Wizard/WizardContext.js.map +1 -0
  29. package/dist/esm/next/components/Wizard/WizardFooter.d.ts +28 -0
  30. package/dist/esm/next/components/Wizard/WizardFooter.d.ts.map +1 -0
  31. package/dist/esm/next/components/Wizard/WizardFooter.js +11 -0
  32. package/dist/esm/next/components/Wizard/WizardFooter.js.map +1 -0
  33. package/dist/esm/next/components/Wizard/WizardHeader.d.ts +21 -0
  34. package/dist/esm/next/components/Wizard/WizardHeader.d.ts.map +1 -0
  35. package/dist/esm/next/components/Wizard/WizardHeader.js +13 -0
  36. package/dist/esm/next/components/Wizard/WizardHeader.js.map +1 -0
  37. package/dist/esm/next/components/Wizard/WizardNav.d.ts +15 -0
  38. package/dist/esm/next/components/Wizard/WizardNav.d.ts.map +1 -0
  39. package/dist/esm/next/components/Wizard/WizardNav.js +13 -0
  40. package/dist/esm/next/components/Wizard/WizardNav.js.map +1 -0
  41. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts +25 -0
  42. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts.map +1 -0
  43. package/dist/esm/next/components/Wizard/WizardNavItem.js +33 -0
  44. package/dist/esm/next/components/Wizard/WizardNavItem.js.map +1 -0
  45. package/dist/esm/next/components/Wizard/WizardStep.d.ts +20 -0
  46. package/dist/esm/next/components/Wizard/WizardStep.d.ts.map +1 -0
  47. package/dist/esm/next/components/Wizard/WizardStep.js +5 -0
  48. package/dist/esm/next/components/Wizard/WizardStep.js.map +1 -0
  49. package/dist/esm/next/components/Wizard/WizardToggle.d.ts +26 -0
  50. package/dist/esm/next/components/Wizard/WizardToggle.d.ts.map +1 -0
  51. package/dist/esm/next/components/Wizard/WizardToggle.js +81 -0
  52. package/dist/esm/next/components/Wizard/WizardToggle.js.map +1 -0
  53. package/dist/esm/next/components/Wizard/hooks/index.d.ts +2 -0
  54. package/dist/esm/next/components/Wizard/hooks/index.d.ts.map +1 -0
  55. package/dist/esm/next/components/Wizard/hooks/index.js +2 -0
  56. package/dist/esm/next/components/Wizard/hooks/index.js.map +1 -0
  57. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts +8 -0
  58. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -0
  59. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js +20 -0
  60. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js.map +1 -0
  61. package/dist/esm/next/components/Wizard/index.d.ts +12 -0
  62. package/dist/esm/next/components/Wizard/index.d.ts.map +1 -0
  63. package/dist/esm/next/components/Wizard/index.js +12 -0
  64. package/dist/esm/next/components/Wizard/index.js.map +1 -0
  65. package/dist/esm/next/components/Wizard/types.d.ts +69 -0
  66. package/dist/esm/next/components/Wizard/types.d.ts.map +1 -0
  67. package/dist/esm/next/components/Wizard/types.js +20 -0
  68. package/dist/esm/next/components/Wizard/types.js.map +1 -0
  69. package/dist/esm/next/components/Wizard/utils.d.ts +15 -0
  70. package/dist/esm/next/components/Wizard/utils.d.ts.map +1 -0
  71. package/dist/esm/next/components/Wizard/utils.js +35 -0
  72. package/dist/esm/next/components/Wizard/utils.js.map +1 -0
  73. package/dist/esm/next/components/index.d.ts +1 -1
  74. package/dist/esm/next/components/index.d.ts.map +1 -1
  75. package/dist/esm/next/components/index.js +1 -1
  76. package/dist/esm/next/components/index.js.map +1 -1
  77. package/dist/js/components/LoginPage/LoginFooterItem.d.ts +3 -3
  78. package/dist/js/components/LoginPage/LoginForm.d.ts +18 -18
  79. package/dist/js/components/LoginPage/LoginHeader.d.ts +1 -1
  80. package/dist/js/components/LoginPage/LoginMainBody.d.ts +2 -2
  81. package/dist/js/components/LoginPage/LoginMainFooter.d.ts +5 -5
  82. package/dist/js/components/LoginPage/LoginMainFooterBandItem.d.ts +2 -2
  83. package/dist/js/components/LoginPage/LoginMainFooterLinksItem.d.ts +5 -5
  84. package/dist/js/components/LoginPage/LoginMainHeader.d.ts +6 -4
  85. package/dist/js/components/LoginPage/LoginMainHeader.d.ts.map +1 -1
  86. package/dist/js/components/LoginPage/LoginMainHeader.js +2 -1
  87. package/dist/js/components/LoginPage/LoginMainHeader.js.map +1 -1
  88. package/dist/js/components/LoginPage/LoginPage.d.ts +16 -14
  89. package/dist/js/components/LoginPage/LoginPage.d.ts.map +1 -1
  90. package/dist/js/components/LoginPage/LoginPage.js +2 -2
  91. package/dist/js/components/LoginPage/LoginPage.js.map +1 -1
  92. package/dist/js/next/components/Wizard/Wizard.d.ts +40 -0
  93. package/dist/js/next/components/Wizard/Wizard.d.ts.map +1 -0
  94. package/dist/js/next/components/Wizard/Wizard.js +90 -0
  95. package/dist/js/next/components/Wizard/Wizard.js.map +1 -0
  96. package/dist/js/next/components/Wizard/WizardBody.d.ts +20 -0
  97. package/dist/js/next/components/Wizard/WizardBody.d.ts.map +1 -0
  98. package/dist/js/next/components/Wizard/WizardBody.js +12 -0
  99. package/dist/js/next/components/Wizard/WizardBody.js.map +1 -0
  100. package/dist/js/next/components/Wizard/WizardContext.d.ts +49 -0
  101. package/dist/js/next/components/Wizard/WizardContext.d.ts.map +1 -0
  102. package/dist/js/next/components/Wizard/WizardContext.js +40 -0
  103. package/dist/js/next/components/Wizard/WizardContext.js.map +1 -0
  104. package/dist/js/next/components/Wizard/WizardFooter.d.ts +28 -0
  105. package/dist/js/next/components/Wizard/WizardFooter.d.ts.map +1 -0
  106. package/dist/js/next/components/Wizard/WizardFooter.js +16 -0
  107. package/dist/js/next/components/Wizard/WizardFooter.js.map +1 -0
  108. package/dist/js/next/components/Wizard/WizardHeader.d.ts +21 -0
  109. package/dist/js/next/components/Wizard/WizardHeader.d.ts.map +1 -0
  110. package/dist/js/next/components/Wizard/WizardHeader.js +18 -0
  111. package/dist/js/next/components/Wizard/WizardHeader.js.map +1 -0
  112. package/dist/js/next/components/Wizard/WizardNav.d.ts +15 -0
  113. package/dist/js/next/components/Wizard/WizardNav.d.ts.map +1 -0
  114. package/dist/js/next/components/Wizard/WizardNav.js +18 -0
  115. package/dist/js/next/components/Wizard/WizardNav.js.map +1 -0
  116. package/dist/js/next/components/Wizard/WizardNavItem.d.ts +25 -0
  117. package/dist/js/next/components/Wizard/WizardNavItem.d.ts.map +1 -0
  118. package/dist/js/next/components/Wizard/WizardNavItem.js +37 -0
  119. package/dist/js/next/components/Wizard/WizardNavItem.js.map +1 -0
  120. package/dist/js/next/components/Wizard/WizardStep.d.ts +20 -0
  121. package/dist/js/next/components/Wizard/WizardStep.d.ts.map +1 -0
  122. package/dist/js/next/components/Wizard/WizardStep.js +10 -0
  123. package/dist/js/next/components/Wizard/WizardStep.js.map +1 -0
  124. package/dist/js/next/components/Wizard/WizardToggle.d.ts +26 -0
  125. package/dist/js/next/components/Wizard/WizardToggle.d.ts.map +1 -0
  126. package/dist/js/next/components/Wizard/WizardToggle.js +86 -0
  127. package/dist/js/next/components/Wizard/WizardToggle.js.map +1 -0
  128. package/dist/js/next/components/Wizard/hooks/index.d.ts +2 -0
  129. package/dist/js/next/components/Wizard/hooks/index.d.ts.map +1 -0
  130. package/dist/js/next/components/Wizard/hooks/index.js +6 -0
  131. package/dist/js/next/components/Wizard/hooks/index.js.map +1 -0
  132. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts +8 -0
  133. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -0
  134. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js +25 -0
  135. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js.map +1 -0
  136. package/dist/js/next/components/Wizard/index.d.ts +12 -0
  137. package/dist/js/next/components/Wizard/index.d.ts.map +1 -0
  138. package/dist/js/next/components/Wizard/index.js +17 -0
  139. package/dist/js/next/components/Wizard/index.js.map +1 -0
  140. package/dist/js/next/components/Wizard/types.d.ts +69 -0
  141. package/dist/js/next/components/Wizard/types.d.ts.map +1 -0
  142. package/dist/js/next/components/Wizard/types.js +29 -0
  143. package/dist/js/next/components/Wizard/types.js.map +1 -0
  144. package/dist/js/next/components/Wizard/utils.d.ts +15 -0
  145. package/dist/js/next/components/Wizard/utils.d.ts.map +1 -0
  146. package/dist/js/next/components/Wizard/utils.js +41 -0
  147. package/dist/js/next/components/Wizard/utils.js.map +1 -0
  148. package/dist/js/next/components/index.d.ts +1 -1
  149. package/dist/js/next/components/index.d.ts.map +1 -1
  150. package/dist/js/next/components/index.js +1 -1
  151. package/dist/js/next/components/index.js.map +1 -1
  152. package/dist/umd/react-core.css +208 -208
  153. package/dist/umd/react-core.js +4 -3
  154. package/dist/umd/react-core.min.js +4 -4
  155. package/package.json +6 -6
  156. package/src/components/Card/examples/Card.md +67 -38
  157. package/src/components/LoginPage/LoginFooterItem.tsx +3 -3
  158. package/src/components/LoginPage/LoginForm.tsx +18 -18
  159. package/src/components/LoginPage/LoginHeader.tsx +1 -1
  160. package/src/components/LoginPage/LoginMainBody.tsx +2 -2
  161. package/src/components/LoginPage/LoginMainFooter.tsx +5 -5
  162. package/src/components/LoginPage/LoginMainFooterBandItem.tsx +2 -2
  163. package/src/components/LoginPage/LoginMainFooterLinksItem.tsx +5 -5
  164. package/src/components/LoginPage/LoginMainHeader.tsx +8 -4
  165. package/src/components/LoginPage/LoginPage.tsx +18 -15
  166. package/src/components/LoginPage/examples/LoginPage.md +8 -0
  167. package/src/components/LoginPage/examples/LoginPageBasic.tsx +0 -1
  168. package/src/components/LoginPage/examples/LoginPageLanguageSelect.tsx +194 -0
  169. package/src/components/LoginPage/examples/LoginPageShowHidePassword.tsx +0 -1
  170. package/src/components/Wizard/examples/Wizard.md +21 -4
  171. package/src/demos/CardDemos.md +4 -0
  172. package/src/next/components/Wizard/Wizard.tsx +184 -0
  173. package/src/next/components/Wizard/WizardBody.tsx +34 -0
  174. package/src/next/components/Wizard/WizardContext.tsx +104 -0
  175. package/src/next/components/Wizard/WizardFooter.tsx +63 -0
  176. package/src/next/components/Wizard/WizardHeader.tsx +53 -0
  177. package/src/next/components/Wizard/WizardNav.tsx +41 -0
  178. package/src/next/components/Wizard/WizardNavItem.tsx +103 -0
  179. package/src/next/components/Wizard/WizardStep.tsx +23 -0
  180. package/src/next/components/Wizard/WizardToggle.tsx +209 -0
  181. package/src/next/components/Wizard/__tests__/Wizard.test.tsx +298 -0
  182. package/src/next/components/Wizard/examples/Wizard.md +125 -0
  183. package/src/next/components/Wizard/examples/WizardBasic.tsx +22 -0
  184. package/src/next/components/Wizard/examples/WizardCustomNav.tsx +46 -0
  185. package/src/next/components/Wizard/examples/WizardKitchenSink.tsx +170 -0
  186. package/src/next/components/Wizard/hooks/index.ts +1 -0
  187. package/src/next/components/Wizard/hooks/useWizardFooter.tsx +22 -0
  188. package/src/next/components/Wizard/index.ts +11 -0
  189. package/src/next/components/Wizard/types.tsx +101 -0
  190. package/src/next/components/Wizard/utils.ts +47 -0
  191. package/src/next/components/index.ts +1 -1
@@ -0,0 +1,298 @@
1
+ import React from 'react';
2
+
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+
6
+ import { DefaultWizardNavProps, WizardControlStep, DefaultWizardFooterProps } from '../types';
7
+ import { WizardNav, WizardNavItem, Wizard, WizardStep } from '../';
8
+
9
+ describe('Wizard', () => {
10
+ it('renders step when child is of type WizardStep', () => {
11
+ render(
12
+ <Wizard>
13
+ <WizardStep id="test-step" name="Test step">
14
+ Step content
15
+ </WizardStep>
16
+ </Wizard>
17
+ );
18
+
19
+ expect(screen.getByRole('button', { name: 'Test step' })).toBeVisible();
20
+ expect(screen.getByText('Step content')).toBeVisible();
21
+ });
22
+
23
+ it('renders step when child has required props; name, id, children', () => {
24
+ const CustomStep = props => <div {...props} />;
25
+
26
+ render(
27
+ <Wizard>
28
+ <CustomStep id="test-step" name="Test step">
29
+ Custom step content
30
+ </CustomStep>
31
+ </Wizard>
32
+ );
33
+
34
+ expect(screen.getByRole('button', { name: 'Test step' })).toBeVisible();
35
+ expect(screen.getByText('Custom step content')).toBeVisible();
36
+ });
37
+
38
+ it('renders a header when specified', () => {
39
+ render(
40
+ <Wizard header="Some header">
41
+ <WizardStep id="test-step" name="Test step" />
42
+ </Wizard>
43
+ );
44
+
45
+ expect(screen.getByText('Some header')).toBeVisible();
46
+ });
47
+
48
+ it('renders default footer without specifying the footer prop', () => {
49
+ render(
50
+ <Wizard>
51
+ <WizardStep id="test-step" name="Test step" />
52
+ </Wizard>
53
+ );
54
+
55
+ expect(screen.getByRole('button', { name: 'Next' })).toBeVisible();
56
+ expect(screen.getByRole('button', { name: 'Back' })).toBeVisible();
57
+ expect(screen.getByRole('button', { name: 'Cancel' })).toBeVisible();
58
+ });
59
+
60
+ it('renders default footer with custom props', () => {
61
+ const footer: DefaultWizardFooterProps = {
62
+ nextButtonText: <>Proceed with caution</>,
63
+ backButtonText: 'Turn back!',
64
+ cancelButtonText: 'Leave now!'
65
+ };
66
+
67
+ render(
68
+ <Wizard footer={footer}>
69
+ <WizardStep id="test-step" name="Test step" />
70
+ </Wizard>
71
+ );
72
+
73
+ expect(screen.getByRole('button', { name: 'Proceed with caution' })).toBeVisible();
74
+ expect(screen.getByRole('button', { name: 'Turn back!' })).toBeVisible();
75
+ expect(screen.getByRole('button', { name: 'Leave now!' })).toBeVisible();
76
+ });
77
+
78
+ it('renders custom footer', () => {
79
+ render(
80
+ <Wizard footer={<>Some footer</>}>
81
+ <WizardStep id="test-step" name="Test step" />
82
+ </Wizard>
83
+ );
84
+
85
+ expect(screen.getByText('Some footer')).toBeVisible();
86
+ });
87
+
88
+ it('renders default nav without specifying the nav prop', () => {
89
+ render(
90
+ <Wizard>
91
+ <WizardStep id="test-step" name="Test step" />
92
+ </Wizard>
93
+ );
94
+
95
+ expect(screen.getByRole('navigation')).toBeVisible();
96
+ expect(screen.getByRole('button', { name: 'Test step' })).toBeVisible();
97
+ });
98
+
99
+ it('renders default nav with custom props', () => {
100
+ const nav: DefaultWizardNavProps = {
101
+ isExpandable: true,
102
+ ariaLabel: 'Some nav label',
103
+ ariaLabelledBy: 'wizard-id',
104
+ forceStepVisit: true
105
+ };
106
+
107
+ render(
108
+ <Wizard id="wizard-id" nav={nav}>
109
+ <WizardStep id="step-1" name="Test step 1" steps={[<WizardStep id="nested-step" name="Nested step" />]} />
110
+ <WizardStep id="step-2" name="Test step 2" />
111
+ </Wizard>
112
+ );
113
+
114
+ const navElement = screen.getByLabelText('Some nav label');
115
+
116
+ expect(navElement).toBeVisible();
117
+ expect(navElement).toHaveAttribute('aria-labelledby', 'wizard-id');
118
+ expect(screen.getByRole('button', { name: 'Test step 1' }).parentElement).toHaveClass('pf-m-expandable');
119
+ expect(screen.getByRole('button', { name: 'Test step 2' })).toHaveAttribute('disabled');
120
+ });
121
+
122
+ it('renders custom nav', () => {
123
+ const nav = (
124
+ isOpen: boolean,
125
+ steps: WizardControlStep[],
126
+ activeStep: WizardControlStep,
127
+ goToStepByIndex: (index: number) => void
128
+ ) => (
129
+ <WizardNav isOpen={isOpen}>
130
+ {steps.map((step, index) => (
131
+ <WizardNavItem
132
+ key={step.id}
133
+ id={step.id}
134
+ content={step.name}
135
+ isCurrent={activeStep.id === step.id}
136
+ isDisabled={step.isDisabled}
137
+ step={index + 1}
138
+ onNavItemClick={goToStepByIndex}
139
+ />
140
+ ))}
141
+ </WizardNav>
142
+ );
143
+
144
+ render(
145
+ <Wizard nav={nav}>
146
+ <WizardStep id="test-step" name="Test step" />
147
+ </Wizard>
148
+ );
149
+
150
+ expect(screen.getByRole('navigation')).toBeVisible();
151
+ expect(screen.getByRole('button', { name: 'Test step' })).toBeVisible();
152
+ });
153
+
154
+ it('starts at the first step as the active one by default', () => {
155
+ render(
156
+ <Wizard>
157
+ <WizardStep id="step-1" name="Test step 1">
158
+ Step 1 content
159
+ </WizardStep>
160
+ <WizardStep id="step-2" name="Test step 2" />
161
+ </Wizard>
162
+ );
163
+
164
+ expect(screen.getByText('Step 1 content')).toBeVisible();
165
+ expect(screen.getByRole('button', { name: 'Test step 1' })).toHaveClass('pf-m-current');
166
+ });
167
+
168
+ it(`can start at a step that isn't the first by specifying 'startIndex'`, () => {
169
+ render(
170
+ <Wizard startIndex={2}>
171
+ <WizardStep id="step-1" name="Test step 1" />
172
+ <WizardStep id="step-2" name="Test step 2">
173
+ Step 2 content
174
+ </WizardStep>
175
+ </Wizard>
176
+ );
177
+
178
+ expect(screen.getByText('Step 2 content')).toBeVisible();
179
+ expect(screen.getByRole('button', { name: 'Test step 2' })).toHaveClass('pf-m-current');
180
+ });
181
+
182
+ it(`can use custom classNames and spread other props into the wizard's div`, () => {
183
+ render(
184
+ <Wizard className="some-class" data-testid="wizard-id">
185
+ <WizardStep id="test-step" name="Test step" />
186
+ </Wizard>
187
+ );
188
+
189
+ expect(screen.getByTestId('wizard-id')).toHaveClass('some-class');
190
+ });
191
+
192
+ it(`can customize the wizard's height and width`, () => {
193
+ render(
194
+ <Wizard height={500} width={500} data-testid="wizard-id">
195
+ <WizardStep id="test-step" name="Test step" />
196
+ </Wizard>
197
+ );
198
+
199
+ const wizard = screen.getByTestId('wizard-id');
200
+
201
+ expect(wizard).toHaveStyle('height: 500px');
202
+ expect(wizard).toHaveStyle('width: 500px');
203
+ });
204
+
205
+ it('calls onNavByIndex on nav item click', () => {
206
+ const onNavByIndex = jest.fn();
207
+
208
+ render(
209
+ <Wizard onNavByIndex={onNavByIndex}>
210
+ <WizardStep id="step-1" name="Test step 1" />
211
+ <WizardStep id="step-2" name="Test step 2" />
212
+ </Wizard>
213
+ );
214
+
215
+ userEvent.click(screen.getByRole('button', { name: 'Test step 2' }));
216
+ expect(onNavByIndex).toHaveBeenCalled();
217
+ });
218
+
219
+ it('calls onNext and not onSave on next button click when not on the last step', () => {
220
+ const onNext = jest.fn();
221
+ const onSave = jest.fn();
222
+
223
+ render(
224
+ <Wizard onNext={onNext} onSave={onSave}>
225
+ <WizardStep id="step-1" name="Test step 1" />
226
+ <WizardStep id="step-2" name="Test step 2" />
227
+ </Wizard>
228
+ );
229
+
230
+ userEvent.click(screen.getByRole('button', { name: 'Next' }));
231
+
232
+ expect(onNext).toHaveBeenCalled();
233
+ expect(onSave).not.toHaveBeenCalled();
234
+ });
235
+
236
+ it('calls onBack on back button click', () => {
237
+ const onBack = jest.fn();
238
+
239
+ render(
240
+ <Wizard onBack={onBack}>
241
+ <WizardStep id="step-1" name="Test step 1" />
242
+ <WizardStep id="step-2" name="Test step 2" />
243
+ </Wizard>
244
+ );
245
+
246
+ userEvent.click(screen.getByRole('button', { name: 'Test step 2' }));
247
+ userEvent.click(screen.getByRole('button', { name: 'Back' }));
248
+
249
+ expect(onBack).toHaveBeenCalled();
250
+ });
251
+
252
+ it('calls onSave and not onClose on next button click when on the last step', () => {
253
+ const onSave = jest.fn();
254
+ const onClose = jest.fn();
255
+
256
+ render(
257
+ <Wizard onSave={onSave} onClose={onClose}>
258
+ <WizardStep id="step-1" name="Test step 1" />
259
+ <WizardStep id="step-2" name="Test step 2" />
260
+ </Wizard>
261
+ );
262
+
263
+ userEvent.click(screen.getByRole('button', { name: 'Test step 2' }));
264
+ userEvent.click(screen.getByRole('button', { name: 'Next' }));
265
+
266
+ expect(onSave).toHaveBeenCalled();
267
+ expect(onClose).not.toHaveBeenCalled();
268
+ });
269
+
270
+ it('calls onClose when onSave is not specified on next button click when on the last step', () => {
271
+ const onClose = jest.fn();
272
+
273
+ render(
274
+ <Wizard onClose={onClose}>
275
+ <WizardStep id="step-1" name="Test step 1" />
276
+ <WizardStep id="step-2" name="Test step 2" />
277
+ </Wizard>
278
+ );
279
+
280
+ userEvent.click(screen.getByRole('button', { name: 'Test step 2' }));
281
+ userEvent.click(screen.getByRole('button', { name: 'Next' }));
282
+
283
+ expect(onClose).toHaveBeenCalled();
284
+ });
285
+
286
+ it('calls onClose on cancel link click', () => {
287
+ const onClose = jest.fn();
288
+
289
+ render(
290
+ <Wizard onClose={onClose}>
291
+ <WizardStep id="test-step" name="Test step" />
292
+ </Wizard>
293
+ );
294
+
295
+ userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
296
+ expect(onClose).toHaveBeenCalled();
297
+ });
298
+ });
@@ -0,0 +1,125 @@
1
+ ---
2
+ id: Wizard
3
+ section: components
4
+ cssPrefix: pf-c-wizard
5
+ propComponents:
6
+ [
7
+ 'Wizard',
8
+ 'WizardFooter',
9
+ 'WizardToggle',
10
+ 'WizardStep',
11
+ 'WizardBody',
12
+ 'WizardHeader',
13
+ 'WizardNav',
14
+ 'WizardNavItem',
15
+ 'WizardContextProps',
16
+ 'WizardBasicStep',
17
+ 'WizardParentStep',
18
+ 'WizardSubStep',
19
+ 'DefaultWizardNavProps',
20
+ 'DefaultWizardFooterProps',
21
+ ]
22
+ beta: true
23
+ ---
24
+
25
+ import {
26
+ FormGroup,
27
+ TextInput,
28
+ Drawer,
29
+ DrawerContent,
30
+ Button,
31
+ Flex,
32
+ DrawerPanelContent,
33
+ DrawerColorVariant,
34
+ DrawerHead,
35
+ DrawerActions,
36
+ DrawerCloseButton
37
+ } from '@patternfly/react-core';
38
+ import {
39
+ Wizard,
40
+ WizardFooter,
41
+ WizardToggle,
42
+ WizardStep,
43
+ WizardBody,
44
+ useWizardFooter,
45
+ useWizardContext,
46
+ WizardNavItem,
47
+ WizardNav,
48
+ WizardHeader
49
+ } from '@patternfly/react-core/next';
50
+ import { css } from '@patternfly/react-styles';
51
+ import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
52
+
53
+ PatternFly has two implementations of a `Wizard`. This newer `Wizard` takes a more explicit and declarative approach compared to the older implementation, which can be found under the [React](/components/wizard/react) tab.
54
+
55
+ ## Examples
56
+
57
+ ### Basic
58
+
59
+ ```ts file="./WizardBasic.tsx"
60
+ ```
61
+
62
+ ### Custom navigation
63
+
64
+ The `Wizard`'s `nav` property can be used to build your own navigation.
65
+
66
+ ```
67
+ /** Callback for the Wizard's 'nav' property. Returns element which replaces the Wizard's default navigation. */
68
+ export type CustomWizardNavFunction = (
69
+ isOpen: boolean,
70
+ steps: WizardControlStep[],
71
+ activeStep: WizardControlStep,
72
+ goToStepByIndex: (index: number) => void
73
+ ) => React.ReactElement<WizardNavProps>;
74
+
75
+ /** Encompasses all step type variants that are internally controlled by the Wizard */
76
+ type WizardControlStep = WizardBasicStep | WizardParentStep | WizardSubStep;
77
+ ```
78
+
79
+ ```ts file="./WizardCustomNav.tsx"
80
+ ```
81
+
82
+ ### Kitchen sink
83
+
84
+ Includes a header, custom footer, sub-steps, step content with a drawer, custom nav item, and nav prevention until step visitation.
85
+
86
+ Custom operations when navigating between steps can be achieved by utilizing `onNext`, `onBack` or `onNavByIndex` properties whose callback functions return the 'id' and 'name' of the currently focused step (currentStep), and the previously focused step (previousStep).
87
+
88
+ ```
89
+ /** Callback for the Wizard's 'onNext', 'onBack', and 'onNavByIndex' properties */
90
+ type WizardNavStepFunction = (currentStep: WizardNavStepData, previousStep: WizardNavStepData) => void;
91
+
92
+ /** Data returned for either parameter of WizardNavStepFunction */
93
+ type WizardNavStepData = Pick<WizardControlStep, 'id' | 'name'>;
94
+ ```
95
+
96
+ ```ts file="./WizardKitchenSink.tsx"
97
+ ```
98
+
99
+ ## Hooks
100
+
101
+ ### useWizardFooter
102
+
103
+ Used to set a unique footer for the wizard on any given step. See step 3 of [Kitchen sink](#kitchen-sink) for a live example.
104
+
105
+ ```noLive
106
+ import { useWizardFooter } from '@patternfly/react-core/next';
107
+
108
+ const StepContent = () => {
109
+ useWizardFooter(<>Some footer</>);
110
+ return <>Step content</>;
111
+ }
112
+ ```
113
+
114
+ ### useWizardContext
115
+
116
+ Used to access any property of [WizardContext](#wizardcontextprops):
117
+
118
+ ```noLive
119
+ import { useWizardContext } from '@patternfly/react-core/next';
120
+
121
+ const StepContent = () => {
122
+ const { activeStep } = useWizardContext();
123
+ return <>This is the active step: {activeStep}</>;
124
+ }
125
+ ```
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { Wizard, WizardStep } from '@patternfly/react-core/next';
3
+
4
+ export const WizardBasic: React.FunctionComponent = () => (
5
+ <Wizard height={400}>
6
+ <WizardStep name="First step" id="basic-first-step">
7
+ <p>Step 1 content</p>
8
+ </WizardStep>
9
+ <WizardStep name="Second step" id="basic-second-step">
10
+ <p>Step 2 content</p>
11
+ </WizardStep>
12
+ <WizardStep name="Third step" id="basic-third-step">
13
+ <p>Step 3 content</p>
14
+ </WizardStep>
15
+ <WizardStep name="Fourth step" id="basic-fourth-step">
16
+ <p>Step 4 content</p>{' '}
17
+ </WizardStep>
18
+ <WizardStep name="Review" id="basic-review-step" nextButtonText="Finish">
19
+ <p>Review step content</p>
20
+ </WizardStep>
21
+ </Wizard>
22
+ );
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import {
3
+ Wizard,
4
+ WizardStep,
5
+ WizardControlStep,
6
+ CustomWizardNavFunction,
7
+ WizardNav,
8
+ WizardNavItem
9
+ } from '@patternfly/react-core/next';
10
+
11
+ export const WizardCustomNav: React.FunctionComponent = () => {
12
+ const nav: CustomWizardNavFunction = (
13
+ isOpen: boolean,
14
+ steps: WizardControlStep[],
15
+ activeStep: WizardControlStep,
16
+ goToStepByIndex: (index: number) => void
17
+ ) => (
18
+ <WizardNav isOpen={isOpen}>
19
+ {steps.map((step, index) => (
20
+ <WizardNavItem
21
+ key={step.id}
22
+ id={step.id}
23
+ content={step.name}
24
+ isCurrent={activeStep.id === step.id}
25
+ isDisabled={step.isDisabled}
26
+ step={index + 1}
27
+ onNavItemClick={goToStepByIndex}
28
+ />
29
+ ))}
30
+ </WizardNav>
31
+ );
32
+
33
+ return (
34
+ <Wizard height={400} nav={nav}>
35
+ <WizardStep name="First step" id="cnav-first-step">
36
+ <p>Did you say...custom nav?</p>
37
+ </WizardStep>
38
+ <WizardStep name="Second step" id="cnav-second-step">
39
+ <p>Step 2 content</p>
40
+ </WizardStep>
41
+ <WizardStep name="Review" id="cnav-review-step" nextButtonText="Wrap it up">
42
+ <p>Review step content</p>
43
+ </WizardStep>
44
+ </Wizard>
45
+ );
46
+ };
@@ -0,0 +1,170 @@
1
+ import React from 'react';
2
+
3
+ import {
4
+ FormGroup,
5
+ TextInput,
6
+ Drawer,
7
+ DrawerContent,
8
+ Button,
9
+ Flex,
10
+ DrawerPanelContent,
11
+ DrawerColorVariant,
12
+ DrawerHead,
13
+ DrawerActions,
14
+ DrawerCloseButton
15
+ } from '@patternfly/react-core';
16
+ import {
17
+ Wizard,
18
+ WizardStep,
19
+ WizardBody,
20
+ WizardFooter,
21
+ WizardStepProps,
22
+ WizardControlStep,
23
+ useWizardFooter,
24
+ useWizardContext,
25
+ WizardNavStepFunction,
26
+ WizardNavStepData,
27
+ WizardNavItem,
28
+ WizardHeader
29
+ } from '@patternfly/react-core/next';
30
+ import { css } from '@patternfly/react-styles';
31
+ import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
32
+
33
+ const CustomWizardFooter = () => {
34
+ const { activeStep, onNext, onBack, onClose } = useWizardContext();
35
+ return <WizardFooter activeStep={activeStep} onNext={onNext} onBack={onBack} onClose={onClose} />;
36
+ };
37
+
38
+ const CustomNavItem = () => {
39
+ const { steps, activeStep, goToStepByIndex } = useWizardContext();
40
+ const step = (steps.find(step => step.id === 'third-step') || {}) as WizardControlStep;
41
+
42
+ return (
43
+ <WizardNavItem
44
+ id={step.id}
45
+ content={<span style={{ color: 'var(--pf-global--default-color--200)' }}>Custom item</span>}
46
+ isCurrent={activeStep.id === step.id}
47
+ step={steps.indexOf(step) + 1}
48
+ isDisabled={step.isDisabled || !step.visited}
49
+ onNavItemClick={goToStepByIndex}
50
+ />
51
+ );
52
+ };
53
+
54
+ const StepContentWithDrawer = () => {
55
+ const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false);
56
+
57
+ return (
58
+ <Drawer isInline isExpanded={isDrawerExpanded}>
59
+ <DrawerContent
60
+ panelContent={
61
+ <DrawerPanelContent widths={{ default: 'width_33' }} colorVariant={DrawerColorVariant.light200}>
62
+ <DrawerHead>
63
+ <span tabIndex={isDrawerExpanded ? 0 : -1}>drawer content</span>
64
+ <DrawerActions>
65
+ <DrawerCloseButton onClick={() => setIsDrawerExpanded(false)} />
66
+ </DrawerActions>
67
+ </DrawerHead>
68
+ </DrawerPanelContent>
69
+ }
70
+ >
71
+ <WizardBody>
72
+ <Flex direction={{ default: 'column' }}>
73
+ {!isDrawerExpanded && (
74
+ <Button isInline variant="link" onClick={() => setIsDrawerExpanded(prevExpanded => !prevExpanded)}>
75
+ Open drawer
76
+ </Button>
77
+ )}
78
+
79
+ <FormGroup fieldId="some-text-input" label="Text input label">
80
+ <TextInput aria-label="Some Text Input Label" />
81
+ </FormGroup>
82
+ </Flex>
83
+ </WizardBody>
84
+ </DrawerContent>
85
+ </Drawer>
86
+ );
87
+ };
88
+
89
+ const StepWithCustomFooter = () => {
90
+ const { onNext: goToNextStep, onBack, onClose } = useWizardContext();
91
+ const [isLoading, setIsLoading] = React.useState(false);
92
+
93
+ async function onNext(goToStep: () => void) {
94
+ setIsLoading(true);
95
+ await new Promise(resolve => setTimeout(resolve, 2000));
96
+ setIsLoading(false);
97
+
98
+ goToStep();
99
+ }
100
+
101
+ const footer = React.useMemo(
102
+ () => (
103
+ <div className={css(styles.wizardFooter)}>
104
+ <Button
105
+ variant="primary"
106
+ type="submit"
107
+ onClick={() => onNext(goToNextStep)}
108
+ isLoading={isLoading}
109
+ isDisabled={isLoading}
110
+ >
111
+ Async Next
112
+ </Button>
113
+ <Button variant="secondary" onClick={onBack}>
114
+ Back
115
+ </Button>
116
+ <Button variant="link" onClick={onClose}>
117
+ Cancel
118
+ </Button>
119
+ </div>
120
+ ),
121
+ [isLoading, onBack, onClose, goToNextStep]
122
+ );
123
+ useWizardFooter(footer);
124
+
125
+ return <>Step 3 content w/ custom async footer</>;
126
+ };
127
+
128
+ const CustomStepFour = (props: WizardStepProps) => <div {...props} id={props.id.toString()} />;
129
+
130
+ export const WizardKitchenSink: React.FunctionComponent = () => {
131
+ const onNext: WizardNavStepFunction = (currentStep: WizardNavStepData, previousStep: WizardNavStepData) => {
132
+ // eslint-disable-next-line no-console
133
+ console.log('currentStep: ', currentStep, '\n previousStep: ', previousStep);
134
+ };
135
+
136
+ return (
137
+ <Wizard
138
+ height={400}
139
+ header={<WizardHeader title="You're a wizard, Harry" closeButtonAriaLabel="Close header" />}
140
+ footer={<CustomWizardFooter />}
141
+ nav={{ forceStepVisit: true, isExpandable: true }}
142
+ onNext={onNext}
143
+ >
144
+ <WizardStep id="sink-first-step" name="First step" body={null}>
145
+ <StepContentWithDrawer />
146
+ </WizardStep>
147
+ <WizardStep
148
+ id="sink-second-step"
149
+ name="Second step"
150
+ steps={[
151
+ <WizardStep id="sink-sub-step-one" key="substep-one" name="Substep 1">
152
+ Substep 1 content
153
+ </WizardStep>,
154
+ <WizardStep id="sink-sub-step-two" key="substep-two" name="Substep 2">
155
+ Substep 2 content
156
+ </WizardStep>
157
+ ]}
158
+ />
159
+ <WizardStep id="sink-third-step" name="Third step" navItem={<CustomNavItem />}>
160
+ <StepWithCustomFooter />
161
+ </WizardStep>
162
+ <CustomStepFour id="sink-fourth-step" name="Fourth step">
163
+ <WizardBody>Step 4 content</WizardBody>
164
+ </CustomStepFour>
165
+ <WizardStep id="sink-review-step" name="Review" nextButtonText="Submit">
166
+ Review step content
167
+ </WizardStep>
168
+ </Wizard>
169
+ );
170
+ };
@@ -0,0 +1 @@
1
+ export { useWizardFooter } from './useWizardFooter';
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { useWizardContext } from '../WizardContext';
3
+
4
+ /**
5
+ * Set a unique footer for the wizard. stepId is only required if inactive steps are hidden instead of unmounted.
6
+ * @param footer
7
+ * @param stepId
8
+ */
9
+ export const useWizardFooter = (footer: React.ReactElement, stepId?: string | number) => {
10
+ const { activeStep, setFooter } = useWizardContext();
11
+
12
+ React.useEffect(() => {
13
+ if (!stepId || activeStep.id === stepId) {
14
+ setFooter(footer);
15
+
16
+ // Reset the footer on unmount.
17
+ return () => {
18
+ setFooter(null);
19
+ };
20
+ }
21
+ }, [activeStep, footer, setFooter, stepId]);
22
+ };
@@ -0,0 +1,11 @@
1
+ export * from './Wizard';
2
+ export * from './WizardBody';
3
+ export * from './WizardFooter';
4
+ export * from './WizardToggle';
5
+ export * from './WizardStep';
6
+ export * from './WizardNav';
7
+ export * from './WizardNavItem';
8
+ export * from './WizardHeader';
9
+ export * from './hooks';
10
+ export * from './types';
11
+ export { useWizardContext } from './WizardContext';