@patternfly/react-core 5.0.0-alpha.12 → 5.0.0-alpha.14
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.
- package/CHANGELOG.md +10 -0
- package/components/Accordion/Accordion.d.ts.map +1 -1
- package/components/Accordion/Accordion.js +2 -2
- package/components/Accordion/Accordion.js.map +1 -1
- package/components/Alert/Alert.d.ts +0 -2
- package/components/Alert/Alert.d.ts.map +1 -1
- package/components/Alert/Alert.js +2 -2
- package/components/Alert/Alert.js.map +1 -1
- package/components/Menu/Menu.d.ts +0 -2
- package/components/Menu/Menu.d.ts.map +1 -1
- package/components/Menu/Menu.js +3 -3
- package/components/Menu/Menu.js.map +1 -1
- package/components/Menu/MenuGroup.js +1 -1
- package/components/Menu/MenuGroup.js.map +1 -1
- package/components/Menu/MenuItem.d.ts +1 -1
- package/components/Menu/MenuItem.d.ts.map +1 -1
- package/components/Menu/MenuItem.js +3 -3
- package/components/Menu/MenuItem.js.map +1 -1
- package/components/Menu/MenuList.d.ts +2 -0
- package/components/Menu/MenuList.d.ts.map +1 -1
- package/components/Menu/MenuList.js +2 -2
- package/components/Menu/MenuList.js.map +1 -1
- package/components/NumberInput/NumberInput.d.ts +0 -2
- package/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/components/NumberInput/NumberInput.js +1 -4
- package/components/NumberInput/NumberInput.js.map +1 -1
- package/components/Page/PageGroup.d.ts +1 -1
- package/components/Page/PageGroup.d.ts.map +1 -1
- package/components/Page/PageGroup.js +1 -1
- package/components/Page/PageGroup.js.map +1 -1
- package/components/Page/PageNavigation.d.ts +1 -1
- package/components/Page/PageNavigation.d.ts.map +1 -1
- package/components/Page/PageNavigation.js +1 -1
- package/components/Page/PageNavigation.js.map +1 -1
- package/components/ProgressStepper/ProgressStep.d.ts.map +1 -1
- package/components/ProgressStepper/ProgressStep.js +3 -1
- package/components/ProgressStepper/ProgressStep.js.map +1 -1
- package/components/Wizard/Wizard.d.ts +6 -2
- package/components/Wizard/Wizard.d.ts.map +1 -1
- package/components/Wizard/Wizard.js.map +1 -1
- package/components/Wizard/WizardBody.d.ts +6 -2
- package/components/Wizard/WizardBody.d.ts.map +1 -1
- package/components/Wizard/WizardBody.js +28 -1
- package/components/Wizard/WizardBody.js.map +1 -1
- package/components/Wizard/WizardToggle.d.ts +6 -2
- package/components/Wizard/WizardToggle.d.ts.map +1 -1
- package/components/Wizard/WizardToggle.js.map +1 -1
- package/dist/esm/components/Accordion/Accordion.d.ts.map +1 -1
- package/dist/esm/components/Accordion/Accordion.js +2 -2
- package/dist/esm/components/Accordion/Accordion.js.map +1 -1
- package/dist/esm/components/Alert/Alert.d.ts +0 -2
- package/dist/esm/components/Alert/Alert.d.ts.map +1 -1
- package/dist/esm/components/Alert/Alert.js +2 -2
- package/dist/esm/components/Alert/Alert.js.map +1 -1
- package/dist/esm/components/Menu/Menu.d.ts +0 -2
- package/dist/esm/components/Menu/Menu.d.ts.map +1 -1
- package/dist/esm/components/Menu/Menu.js +3 -3
- package/dist/esm/components/Menu/Menu.js.map +1 -1
- package/dist/esm/components/Menu/MenuGroup.js +1 -1
- package/dist/esm/components/Menu/MenuGroup.js.map +1 -1
- package/dist/esm/components/Menu/MenuItem.d.ts +1 -1
- package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
- package/dist/esm/components/Menu/MenuItem.js +3 -3
- package/dist/esm/components/Menu/MenuItem.js.map +1 -1
- package/dist/esm/components/Menu/MenuList.d.ts +2 -0
- package/dist/esm/components/Menu/MenuList.d.ts.map +1 -1
- package/dist/esm/components/Menu/MenuList.js +2 -2
- package/dist/esm/components/Menu/MenuList.js.map +1 -1
- package/dist/esm/components/NumberInput/NumberInput.d.ts +0 -2
- package/dist/esm/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/esm/components/NumberInput/NumberInput.js +1 -4
- package/dist/esm/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/esm/components/Page/PageGroup.d.ts +1 -1
- package/dist/esm/components/Page/PageGroup.d.ts.map +1 -1
- package/dist/esm/components/Page/PageGroup.js +1 -1
- package/dist/esm/components/Page/PageGroup.js.map +1 -1
- package/dist/esm/components/Page/PageNavigation.d.ts +1 -1
- package/dist/esm/components/Page/PageNavigation.d.ts.map +1 -1
- package/dist/esm/components/Page/PageNavigation.js +1 -1
- package/dist/esm/components/Page/PageNavigation.js.map +1 -1
- package/dist/esm/components/ProgressStepper/ProgressStep.d.ts.map +1 -1
- package/dist/esm/components/ProgressStepper/ProgressStep.js +3 -1
- package/dist/esm/components/ProgressStepper/ProgressStep.js.map +1 -1
- package/dist/esm/components/Wizard/Wizard.d.ts +6 -2
- package/dist/esm/components/Wizard/Wizard.d.ts.map +1 -1
- package/dist/esm/components/Wizard/Wizard.js.map +1 -1
- package/dist/esm/components/Wizard/WizardBody.d.ts +6 -2
- package/dist/esm/components/Wizard/WizardBody.d.ts.map +1 -1
- package/dist/esm/components/Wizard/WizardBody.js +28 -1
- package/dist/esm/components/Wizard/WizardBody.js.map +1 -1
- package/dist/esm/components/Wizard/WizardToggle.d.ts +6 -2
- package/dist/esm/components/Wizard/WizardToggle.d.ts.map +1 -1
- package/dist/esm/components/Wizard/WizardToggle.js.map +1 -1
- package/dist/esm/next/components/Wizard/WizardBody.d.ts +7 -3
- package/dist/esm/next/components/Wizard/WizardBody.d.ts.map +1 -1
- package/dist/esm/next/components/Wizard/WizardBody.js +35 -2
- package/dist/esm/next/components/Wizard/WizardBody.js.map +1 -1
- package/dist/js/components/Accordion/Accordion.d.ts.map +1 -1
- package/dist/js/components/Accordion/Accordion.js +2 -2
- package/dist/js/components/Accordion/Accordion.js.map +1 -1
- package/dist/js/components/Alert/Alert.d.ts +0 -2
- package/dist/js/components/Alert/Alert.d.ts.map +1 -1
- package/dist/js/components/Alert/Alert.js +2 -2
- package/dist/js/components/Alert/Alert.js.map +1 -1
- package/dist/js/components/Menu/Menu.d.ts +0 -2
- package/dist/js/components/Menu/Menu.d.ts.map +1 -1
- package/dist/js/components/Menu/Menu.js +3 -3
- package/dist/js/components/Menu/Menu.js.map +1 -1
- package/dist/js/components/Menu/MenuGroup.js +1 -1
- package/dist/js/components/Menu/MenuGroup.js.map +1 -1
- package/dist/js/components/Menu/MenuItem.d.ts +1 -1
- package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
- package/dist/js/components/Menu/MenuItem.js +3 -3
- package/dist/js/components/Menu/MenuItem.js.map +1 -1
- package/dist/js/components/Menu/MenuList.d.ts +2 -0
- package/dist/js/components/Menu/MenuList.d.ts.map +1 -1
- package/dist/js/components/Menu/MenuList.js +2 -2
- package/dist/js/components/Menu/MenuList.js.map +1 -1
- package/dist/js/components/NumberInput/NumberInput.d.ts +0 -2
- package/dist/js/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/js/components/NumberInput/NumberInput.js +1 -4
- package/dist/js/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/js/components/Page/PageGroup.d.ts +1 -1
- package/dist/js/components/Page/PageGroup.d.ts.map +1 -1
- package/dist/js/components/Page/PageGroup.js +1 -1
- package/dist/js/components/Page/PageGroup.js.map +1 -1
- package/dist/js/components/Page/PageNavigation.d.ts +1 -1
- package/dist/js/components/Page/PageNavigation.d.ts.map +1 -1
- package/dist/js/components/Page/PageNavigation.js +1 -1
- package/dist/js/components/Page/PageNavigation.js.map +1 -1
- package/dist/js/components/ProgressStepper/ProgressStep.d.ts.map +1 -1
- package/dist/js/components/ProgressStepper/ProgressStep.js +3 -1
- package/dist/js/components/ProgressStepper/ProgressStep.js.map +1 -1
- package/dist/js/components/Wizard/Wizard.d.ts +6 -2
- package/dist/js/components/Wizard/Wizard.d.ts.map +1 -1
- package/dist/js/components/Wizard/Wizard.js.map +1 -1
- package/dist/js/components/Wizard/WizardBody.d.ts +6 -2
- package/dist/js/components/Wizard/WizardBody.d.ts.map +1 -1
- package/dist/js/components/Wizard/WizardBody.js +28 -1
- package/dist/js/components/Wizard/WizardBody.js.map +1 -1
- package/dist/js/components/Wizard/WizardToggle.d.ts +6 -2
- package/dist/js/components/Wizard/WizardToggle.d.ts.map +1 -1
- package/dist/js/components/Wizard/WizardToggle.js.map +1 -1
- package/dist/js/next/components/Wizard/WizardBody.d.ts +7 -3
- package/dist/js/next/components/Wizard/WizardBody.d.ts.map +1 -1
- package/dist/js/next/components/Wizard/WizardBody.js +35 -2
- package/dist/js/next/components/Wizard/WizardBody.js.map +1 -1
- package/dist/umd/react-core.min.js +1 -1
- package/next/components/Wizard/WizardBody.d.ts +7 -3
- package/next/components/Wizard/WizardBody.d.ts.map +1 -1
- package/next/components/Wizard/WizardBody.js +35 -2
- package/next/components/Wizard/WizardBody.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Accordion/Accordion.tsx +2 -1
- package/src/components/Alert/Alert.tsx +0 -4
- package/src/components/Alert/__tests__/Alert.test.tsx +33 -25
- package/src/components/Menu/Menu.tsx +0 -4
- package/src/components/Menu/MenuGroup.tsx +1 -1
- package/src/components/Menu/MenuItem.tsx +4 -2
- package/src/components/Menu/MenuList.tsx +4 -0
- package/src/components/NumberInput/NumberInput.tsx +0 -6
- package/src/components/NumberInput/__tests__/NumberInput.test.tsx +0 -6
- package/src/components/NumberInput/examples/NumberInputCustomStep.tsx +0 -1
- package/src/components/NumberInput/examples/NumberInputCustomStepAndThreshold.tsx +0 -1
- package/src/components/NumberInput/examples/NumberInputDefault.tsx +0 -1
- package/src/components/NumberInput/examples/NumberInputDisabled.tsx +0 -1
- package/src/components/NumberInput/examples/NumberInputUnit.tsx +0 -2
- package/src/components/NumberInput/examples/NumberInputUnitThreshold.tsx +0 -1
- package/src/components/NumberInput/examples/NumberInputVaryingSizes.tsx +0 -4
- package/src/components/NumberInput/examples/NumberInputWithStatus.tsx +0 -1
- package/src/components/Page/PageGroup.tsx +2 -3
- package/src/components/Page/PageNavigation.tsx +2 -3
- package/src/components/Page/__tests__/Page.test.tsx +6 -1
- package/src/components/Page/__tests__/PageGroup.test.tsx +5 -1
- package/src/components/Page/__tests__/PageNavigation.test.tsx +5 -1
- package/src/components/Page/__tests__/__snapshots__/Page.test.tsx.snap +3 -1
- package/src/components/Page/__tests__/__snapshots__/PageGroup.test.tsx.snap +1 -0
- package/src/components/Page/__tests__/__snapshots__/PageNavigation.test.tsx.snap +1 -0
- package/src/components/ProgressStepper/ProgressStep.tsx +3 -1
- package/src/components/ProgressStepper/__tests__/ProgressStep.test.tsx +2 -2
- package/src/components/Wizard/Wizard.tsx +6 -2
- package/src/components/Wizard/WizardBody.tsx +46 -3
- package/src/components/Wizard/WizardToggle.tsx +6 -2
- package/src/components/Wizard/__tests__/Generated/__snapshots__/WizardBody.test.tsx.snap +0 -2
- package/src/components/Wizard/__tests__/Generated/__snapshots__/WizardToggle.test.tsx.snap +0 -2
- package/src/components/Wizard/__tests__/Wizard.test.tsx +70 -132
- package/src/components/Wizard/__tests__/__snapshots__/Wizard.test.tsx.snap +0 -2
- package/src/components/Wizard/examples/WizardBasic.tsx +26 -1
- package/src/demos/examples/AlertGroup/AlertGroupToastWithNotificationDrawer.tsx +0 -1
- package/src/next/components/Wizard/WizardBody.tsx +57 -8
- package/src/next/components/Wizard/__tests__/WizardBody.test.tsx +0 -10
- package/src/next/components/Wizard/examples/Wizard.md +1 -1
- package/src/next/components/Wizard/examples/WizardBasic.tsx +19 -1
- package/src/next/components/Wizard/examples/WizardKitchenSink.tsx +0 -236
|
@@ -149,11 +149,11 @@ test('Does not renders with aria-labelledBy by default on Component element', ()
|
|
|
149
149
|
|
|
150
150
|
test('Renders with an accessible name that matches children', () => {
|
|
151
151
|
render(
|
|
152
|
-
<ProgressStep id="test-id" titleId="title-id">
|
|
152
|
+
<ProgressStep popoverRender={stepRef => <div>Popover content</div>} id="test-id" titleId="title-id">
|
|
153
153
|
Test
|
|
154
154
|
</ProgressStep>
|
|
155
155
|
);
|
|
156
|
-
expect(screen.getByText('Test')).toHaveAccessibleName('Test');
|
|
156
|
+
expect(screen.getByText('Test')).toHaveAccessibleName('Test Popover content');
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
test('Renders with popoverRender element', () => {
|
|
@@ -78,9 +78,13 @@ export interface WizardProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
78
78
|
navAriaLabel?: string;
|
|
79
79
|
/** Sets aria-labelledby on nav element */
|
|
80
80
|
navAriaLabelledBy?: string;
|
|
81
|
-
/**
|
|
81
|
+
/** Adds an accessible name to the wizard body when the body content overflows and renders
|
|
82
|
+
* a scrollbar.
|
|
83
|
+
*/
|
|
82
84
|
mainAriaLabel?: string;
|
|
83
|
-
/**
|
|
85
|
+
/** Adds an accessible name to the wizard body by passing the the id of one or more elements.
|
|
86
|
+
* The aria-labelledby will only be applied when the body content overflows and renders a scrollbar.
|
|
87
|
+
*/
|
|
84
88
|
mainAriaLabelledBy?: string;
|
|
85
89
|
/** Can remove the default padding around the main body content by setting this to true */
|
|
86
90
|
hasNoBodyPadding?: boolean;
|
|
@@ -4,15 +4,21 @@ import { css } from '@patternfly/react-styles';
|
|
|
4
4
|
import { WizardDrawerWrapper } from './WizardDrawerWrapper';
|
|
5
5
|
import { Drawer, DrawerContent } from '../Drawer';
|
|
6
6
|
import { WizardStep } from './Wizard';
|
|
7
|
+
import { debounce } from '../../helpers/util';
|
|
8
|
+
import { getResizeObserver } from '../../helpers/resizeObserver';
|
|
7
9
|
|
|
8
10
|
export interface WizardBodyProps {
|
|
9
11
|
/** Anything that can be rendered in the Wizard body */
|
|
10
12
|
children: any;
|
|
11
13
|
/** Set to true to remove the default body padding */
|
|
12
14
|
hasNoBodyPadding: boolean;
|
|
13
|
-
/**
|
|
15
|
+
/** Adds an accessible name to the wizard body when the body content overflows and renders
|
|
16
|
+
* a scrollbar.
|
|
17
|
+
*/
|
|
14
18
|
'aria-label'?: string;
|
|
15
|
-
/**
|
|
19
|
+
/** Adds an accessible name to the wizard body by passing the the id of one or more elements.
|
|
20
|
+
* The aria-labelledby will only be applied when the body content overflows and renders a scrollbar.
|
|
21
|
+
*/
|
|
16
22
|
'aria-labelledby': string;
|
|
17
23
|
/** Component used as the primary content container */
|
|
18
24
|
mainComponent?: React.ElementType;
|
|
@@ -37,8 +43,45 @@ export const WizardBody: React.FunctionComponent<WizardBodyProps> = ({
|
|
|
37
43
|
activeStep
|
|
38
44
|
}: WizardBodyProps) => {
|
|
39
45
|
const MainComponent = mainComponent;
|
|
46
|
+
const [hasScrollbar, setHasScrollbar] = React.useState(false);
|
|
47
|
+
const [previousWidth, setPreviousWidth] = React.useState<number | undefined>(undefined);
|
|
48
|
+
const wizardBodyRef = React.useRef(null);
|
|
49
|
+
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
const resize = () => {
|
|
52
|
+
if (wizardBodyRef?.current) {
|
|
53
|
+
const { offsetWidth, offsetHeight, scrollHeight } = wizardBodyRef.current;
|
|
54
|
+
|
|
55
|
+
if (previousWidth !== offsetWidth) {
|
|
56
|
+
setPreviousWidth(offsetWidth);
|
|
57
|
+
setHasScrollbar(offsetHeight < scrollHeight);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const handleResizeWithDelay = debounce(resize, 250);
|
|
63
|
+
let observer = () => {};
|
|
64
|
+
|
|
65
|
+
if (wizardBodyRef?.current) {
|
|
66
|
+
observer = getResizeObserver(wizardBodyRef.current, handleResizeWithDelay);
|
|
67
|
+
const { offsetHeight, scrollHeight } = wizardBodyRef.current;
|
|
68
|
+
|
|
69
|
+
setHasScrollbar(offsetHeight < scrollHeight);
|
|
70
|
+
setPreviousWidth((wizardBodyRef.current as HTMLElement).offsetWidth);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return () => {
|
|
74
|
+
observer();
|
|
75
|
+
};
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
40
78
|
return (
|
|
41
|
-
<MainComponent
|
|
79
|
+
<MainComponent
|
|
80
|
+
{...(mainComponent === 'div' && hasScrollbar && { role: 'region' })}
|
|
81
|
+
{...(hasScrollbar && { 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, tabIndex: 0 })}
|
|
82
|
+
ref={wizardBodyRef}
|
|
83
|
+
className={css(styles.wizardMain)}
|
|
84
|
+
>
|
|
42
85
|
<WizardDrawerWrapper
|
|
43
86
|
hasDrawer={hasDrawer && activeStep.drawerPanelContent}
|
|
44
87
|
wrapper={(children: React.ReactNode) => (
|
|
@@ -23,9 +23,13 @@ export interface WizardToggleProps {
|
|
|
23
23
|
onNavToggle: (isOpen: boolean) => void;
|
|
24
24
|
/** The button's aria-label */
|
|
25
25
|
'aria-label'?: string;
|
|
26
|
-
/**
|
|
26
|
+
/** Adds an accessible name to the wizard body by passing the the id of one or more elements.
|
|
27
|
+
* The aria-labelledby will only be applied when the body content overflows and renders a scrollbar.
|
|
28
|
+
*/
|
|
27
29
|
mainAriaLabelledBy?: string;
|
|
28
|
-
/**
|
|
30
|
+
/** Adds an accessible name to the wizard body when the body content overflows and renders
|
|
31
|
+
* a scrollbar.
|
|
32
|
+
*/
|
|
29
33
|
mainAriaLabel?: string;
|
|
30
34
|
/** If the wizard is in-page */
|
|
31
35
|
isInPage?: boolean;
|
|
@@ -130,37 +130,12 @@ describe('Wizard', () => {
|
|
|
130
130
|
expect(nav).toHaveAttribute('aria-labelledby', 'pf-wizard-title-5');
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
test('wiz with title, navAriaLabel, and mainAriaLabel combination', () => {
|
|
134
|
-
const steps: WizardStep[] = [{ name: 'A', component: <p>Step 1</p> }];
|
|
135
|
-
render(
|
|
136
|
-
<Wizard
|
|
137
|
-
title="Wizard"
|
|
138
|
-
navAriaLabel="nav aria-label"
|
|
139
|
-
mainAriaLabel="main aria-label"
|
|
140
|
-
description="This wizard has a title"
|
|
141
|
-
steps={steps}
|
|
142
|
-
/>
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
expect(screen.getByRole('heading', { name: 'Wizard' })).toBeInTheDocument();
|
|
146
|
-
expect(screen.getByLabelText('nav aria-label')).toBeInTheDocument();
|
|
147
|
-
expect(screen.getByLabelText('main aria-label')).toBeInTheDocument();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
133
|
test('wiz with navAriaLabel and mainAriaLabel but without title', () => {
|
|
151
134
|
const steps: WizardStep[] = [{ name: 'A', component: <p>Step 1</p> }];
|
|
152
135
|
|
|
153
|
-
render(
|
|
154
|
-
<Wizard
|
|
155
|
-
navAriaLabel="nav aria-label"
|
|
156
|
-
mainAriaLabel="main aria-label"
|
|
157
|
-
description="This wizard has a title"
|
|
158
|
-
steps={steps}
|
|
159
|
-
/>
|
|
160
|
-
);
|
|
136
|
+
render(<Wizard navAriaLabel="nav aria-label" description="This wizard has a title" steps={steps} />);
|
|
161
137
|
|
|
162
138
|
expect(screen.getByLabelText('nav aria-label')).toBeInTheDocument();
|
|
163
|
-
expect(screen.getByLabelText('main aria-label')).toBeInTheDocument();
|
|
164
139
|
});
|
|
165
140
|
|
|
166
141
|
test('wiz with navAriaLabel and navAriaLabelledby and without title', () => {
|
|
@@ -170,7 +145,6 @@ describe('Wizard', () => {
|
|
|
170
145
|
<Wizard
|
|
171
146
|
navAriaLabelledBy="nav-aria-labelledby"
|
|
172
147
|
navAriaLabel="nav aria-label"
|
|
173
|
-
mainAriaLabel="main aria-label"
|
|
174
148
|
description="This wizard has a title"
|
|
175
149
|
steps={steps}
|
|
176
150
|
/>
|
|
@@ -182,11 +156,13 @@ describe('Wizard', () => {
|
|
|
182
156
|
expect(nav).toHaveAttribute('aria-labelledby', 'nav-aria-labelledby');
|
|
183
157
|
});
|
|
184
158
|
|
|
185
|
-
test('wiz with
|
|
159
|
+
test('wiz with title, navAriaLabelledBy, and navAriaLabel', () => {
|
|
186
160
|
const steps: WizardStep[] = [{ name: 'A', component: <p>Step 1</p> }];
|
|
187
161
|
render(
|
|
188
162
|
<Wizard
|
|
163
|
+
title="Wiz title"
|
|
189
164
|
navAriaLabel="nav aria-label"
|
|
165
|
+
navAriaLabelledBy="nav-aria-labelledby"
|
|
190
166
|
mainAriaLabel="main aria-label"
|
|
191
167
|
mainAriaLabelledBy="main-aria-labelledby"
|
|
192
168
|
description="This wizard has a title"
|
|
@@ -194,19 +170,17 @@ describe('Wizard', () => {
|
|
|
194
170
|
/>
|
|
195
171
|
);
|
|
196
172
|
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
expect(
|
|
200
|
-
expect(main).toHaveAttribute('aria-labelledby', 'main-aria-labelledby');
|
|
173
|
+
const nav = screen.getByLabelText('nav aria-label');
|
|
174
|
+
expect(nav).toBeInTheDocument();
|
|
175
|
+
expect(nav).toHaveAttribute('aria-labelledby', 'nav-aria-labelledby');
|
|
201
176
|
});
|
|
202
177
|
|
|
203
|
-
test('
|
|
178
|
+
test('Does not render with mainAriaLabel or mainAriaLabelledby by default', () => {
|
|
204
179
|
const steps: WizardStep[] = [{ name: 'A', component: <p>Step 1</p> }];
|
|
205
180
|
render(
|
|
206
181
|
<Wizard
|
|
182
|
+
data-testid="wizard-test-id"
|
|
207
183
|
title="Wiz title"
|
|
208
|
-
navAriaLabel="nav aria-label"
|
|
209
|
-
navAriaLabelledBy="nav-aria-labelledby"
|
|
210
184
|
mainAriaLabel="main aria-label"
|
|
211
185
|
mainAriaLabelledBy="main-aria-labelledby"
|
|
212
186
|
description="This wizard has a title"
|
|
@@ -214,13 +188,9 @@ describe('Wizard', () => {
|
|
|
214
188
|
/>
|
|
215
189
|
);
|
|
216
190
|
|
|
217
|
-
const
|
|
218
|
-
expect(
|
|
219
|
-
expect(
|
|
220
|
-
|
|
221
|
-
const main = screen.getByLabelText('main aria-label');
|
|
222
|
-
expect(main).toBeInTheDocument();
|
|
223
|
-
expect(main).toHaveAttribute('aria-labelledby', 'main-aria-labelledby');
|
|
191
|
+
const main = screen.getByTestId('wizard-test-id');
|
|
192
|
+
expect(main).not.toHaveAttribute('aria-labelledby');
|
|
193
|
+
expect(main).not.toHaveAttribute('aria-label');
|
|
224
194
|
});
|
|
225
195
|
|
|
226
196
|
test('wiz with onCurrentStepChanged setter', async () => {
|
|
@@ -228,17 +198,11 @@ describe('Wizard', () => {
|
|
|
228
198
|
const stepB = { name: 'B', component: <p>Step 2</p> };
|
|
229
199
|
const stepC = { name: 'C', component: <p>Step 3</p> };
|
|
230
200
|
|
|
231
|
-
const steps: WizardStep[] = [
|
|
201
|
+
const steps: WizardStep[] = [stepA, stepB, stepC];
|
|
232
202
|
const setter = jest.fn();
|
|
233
203
|
const user = userEvent.setup();
|
|
234
204
|
|
|
235
|
-
render(
|
|
236
|
-
<Wizard
|
|
237
|
-
title="Wiz title"
|
|
238
|
-
onCurrentStepChanged={ setter }
|
|
239
|
-
steps={steps}
|
|
240
|
-
/>
|
|
241
|
-
);
|
|
205
|
+
render(<Wizard title="Wiz title" onCurrentStepChanged={setter} steps={steps} />);
|
|
242
206
|
expect(setter).toHaveBeenLastCalledWith(expect.objectContaining(stepA));
|
|
243
207
|
await user.click(screen.getByText(/next/i));
|
|
244
208
|
expect(setter).toHaveBeenLastCalledWith(expect.objectContaining(stepB));
|
|
@@ -253,98 +217,72 @@ describe('Wizard', () => {
|
|
|
253
217
|
});
|
|
254
218
|
});
|
|
255
219
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
220
|
+
test('wiz with disabled steps', () => {
|
|
221
|
+
const steps: WizardStep[] = [
|
|
222
|
+
{ name: 'A', component: <p>Step 1</p> },
|
|
223
|
+
{ name: 'B', component: <p>Step 2</p>, isDisabled: true },
|
|
224
|
+
{ name: 'C', component: <p>Step 3</p> },
|
|
225
|
+
{ name: 'E', component: <p>Step 4</p>, isDisabled: true },
|
|
226
|
+
{ name: 'G', component: <p>Step 5</p> }
|
|
227
|
+
];
|
|
263
228
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
render(
|
|
267
|
-
<Wizard
|
|
268
|
-
navAriaLabel="nav aria-label"
|
|
269
|
-
description="This wizard step is disabled"
|
|
270
|
-
steps={steps}
|
|
271
|
-
/>
|
|
272
|
-
);
|
|
229
|
+
render(<Wizard navAriaLabel="nav aria-label" description="This wizard step is disabled" steps={steps} />);
|
|
273
230
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
231
|
+
expect(screen.getByRole('button', { name: 'B' })).toBeDisabled();
|
|
232
|
+
expect(screen.getByRole('button', { name: 'E' })).toBeDisabled();
|
|
233
|
+
});
|
|
277
234
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
235
|
+
test('wiz skip the step disabled when press the next/back button', async () => {
|
|
236
|
+
const steps: WizardStep[] = [
|
|
237
|
+
{ name: 'A', component: <p>Step 1</p> },
|
|
238
|
+
{ name: 'B', component: <p>Step 2</p>, isDisabled: true },
|
|
239
|
+
{ name: 'C', component: <p>Step 3</p> }
|
|
240
|
+
];
|
|
241
|
+
const user = userEvent.setup();
|
|
283
242
|
|
|
284
|
-
|
|
285
|
-
const user = userEvent.setup();
|
|
243
|
+
render(<Wizard navAriaLabel="nav aria-label" description="This wizard step is disabled" steps={steps} />);
|
|
286
244
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
navAriaLabel="nav aria-label"
|
|
290
|
-
description="This wizard step is disabled"
|
|
291
|
-
steps={steps}
|
|
292
|
-
/>
|
|
293
|
-
);
|
|
245
|
+
await user.click(screen.getByRole('button', { name: 'Next' }));
|
|
246
|
+
expect(screen.getByRole('button', { name: 'C' })).toHaveClass('pf-m-current');
|
|
294
247
|
|
|
295
|
-
|
|
296
|
-
|
|
248
|
+
await user.click(screen.getByRole('button', { name: 'Back' }));
|
|
249
|
+
expect(screen.getByRole('button', { name: 'A' })).toHaveClass('pf-m-current');
|
|
250
|
+
});
|
|
297
251
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
{ name: 'B', component: <p>Step 2</p>, isDisabled: true },
|
|
306
|
-
{ name: 'C', component: <p>Step 3</p>}
|
|
307
|
-
];
|
|
308
|
-
const user = userEvent.setup();
|
|
309
|
-
|
|
310
|
-
render(
|
|
311
|
-
<Wizard
|
|
312
|
-
navAriaLabel="nav aria-label"
|
|
313
|
-
description="This wizard step is disabled"
|
|
314
|
-
steps={steps}
|
|
315
|
-
/>
|
|
316
|
-
);
|
|
252
|
+
test('wiz skip the step when click on the nav item disabled', async () => {
|
|
253
|
+
const steps: WizardStep[] = [
|
|
254
|
+
{ name: 'A', component: <p>Step 1</p> },
|
|
255
|
+
{ name: 'B', component: <p>Step 2</p>, isDisabled: true },
|
|
256
|
+
{ name: 'C', component: <p>Step 3</p> }
|
|
257
|
+
];
|
|
258
|
+
const user = userEvent.setup();
|
|
317
259
|
|
|
318
|
-
|
|
319
|
-
const navItemButtonSelected = screen.getByRole('button',{ name: "A" })
|
|
320
|
-
|
|
321
|
-
await user.click(navItemButton);
|
|
260
|
+
render(<Wizard navAriaLabel="nav aria-label" description="This wizard step is disabled" steps={steps} />);
|
|
322
261
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
});
|
|
262
|
+
const navItemButton = screen.getByRole('button', { name: 'B' });
|
|
263
|
+
const navItemButtonSelected = screen.getByRole('button', { name: 'A' });
|
|
326
264
|
|
|
327
|
-
|
|
328
|
-
const steps: WizardStep[] = [
|
|
329
|
-
{
|
|
330
|
-
name: 'A',
|
|
331
|
-
steps: [
|
|
332
|
-
{ name: 'A-1', component: <p>Substep A content</p> },
|
|
333
|
-
{ name: 'A-2', component: <p>Substep B content</p>, isDisabled: true }
|
|
334
|
-
]
|
|
335
|
-
},
|
|
336
|
-
];
|
|
337
|
-
|
|
338
|
-
render(
|
|
339
|
-
<Wizard
|
|
340
|
-
navAriaLabel="nav aria-label"
|
|
341
|
-
description="This wizard step is disabled"
|
|
342
|
-
steps={steps}
|
|
343
|
-
/>
|
|
344
|
-
);
|
|
265
|
+
await user.click(navItemButton);
|
|
345
266
|
|
|
346
|
-
|
|
347
|
-
|
|
267
|
+
expect(navItemButton).not.toHaveClass('pf-m-current');
|
|
268
|
+
expect(navItemButtonSelected).toHaveClass('pf-m-current');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('wiz with disable sub step', () => {
|
|
272
|
+
const steps: WizardStep[] = [
|
|
273
|
+
{
|
|
274
|
+
name: 'A',
|
|
275
|
+
steps: [
|
|
276
|
+
{ name: 'A-1', component: <p>Substep A content</p> },
|
|
277
|
+
{ name: 'A-2', component: <p>Substep B content</p>, isDisabled: true }
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
render(<Wizard navAriaLabel="nav aria-label" description="This wizard step is disabled" steps={steps} />);
|
|
283
|
+
|
|
284
|
+
expect(screen.getByRole('button', { name: 'A-2' })).toBeDisabled();
|
|
285
|
+
});
|
|
348
286
|
|
|
349
287
|
test('startAtStep can be used to externally control the current step of the wizard', async () => {
|
|
350
288
|
const WizardTest = () => {
|
|
@@ -373,7 +311,7 @@ test('startAtStep can be used to externally control the current step of the wiza
|
|
|
373
311
|
|
|
374
312
|
expect(screen.queryByText('Step 2')).not.toBeInTheDocument();
|
|
375
313
|
|
|
376
|
-
await user.click(screen.getByRole('button', { name: 'Increment step'}))
|
|
314
|
+
await user.click(screen.getByRole('button', { name: 'Increment step' }));
|
|
377
315
|
|
|
378
316
|
expect(screen.getByText('Step 2')).toBeVisible();
|
|
379
317
|
});
|
|
@@ -212,7 +212,6 @@ exports[`Wizard Expandable Nav Wizard should match snapshot 1`] = `
|
|
|
212
212
|
</ol>
|
|
213
213
|
</nav>
|
|
214
214
|
<div
|
|
215
|
-
aria-labelledby="pf-wizard-title-1"
|
|
216
215
|
class="pf-c-wizard__main"
|
|
217
216
|
>
|
|
218
217
|
<div
|
|
@@ -460,7 +459,6 @@ exports[`Wizard Wizard should match snapshot 1`] = `
|
|
|
460
459
|
</ol>
|
|
461
460
|
</nav>
|
|
462
461
|
<div
|
|
463
|
-
aria-labelledby="pf-wizard-title-0"
|
|
464
462
|
class="pf-c-wizard__main"
|
|
465
463
|
>
|
|
466
464
|
<div
|
|
@@ -3,7 +3,32 @@ import { Wizard } from '@patternfly/react-core';
|
|
|
3
3
|
|
|
4
4
|
export const WizardBasic: React.FunctionComponent = () => {
|
|
5
5
|
const steps = [
|
|
6
|
-
{
|
|
6
|
+
{
|
|
7
|
+
name: 'First step',
|
|
8
|
+
component: (
|
|
9
|
+
<>
|
|
10
|
+
<p>
|
|
11
|
+
The content of this step overflows and creates a scrollbar, which causes a tabindex of "0", a role of
|
|
12
|
+
"region", and an aria-label or aria-labelledby to be applied.
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum in neque nec pharetra. Duis
|
|
16
|
+
lacinia vel sapien ut imperdiet. Nunc ultrices mollis dictum. Duis tempus, massa nec tincidunt tempor, enim
|
|
17
|
+
ex porttitor odio, eu facilisis dolor tortor id sapien. Etiam sit amet molestie lacus. Nulla facilisi. Duis
|
|
18
|
+
eget finibus ipsum. Quisque dictum enim sed sodales porta. Curabitur eget orci eu risus posuere pulvinar id
|
|
19
|
+
nec turpis. Morbi mattis orci vel posuere tincidunt. Fusce bibendum et libero a auctor.
|
|
20
|
+
</p>
|
|
21
|
+
<p>
|
|
22
|
+
Proin elementum commodo sodales. Quisque eget libero mattis, ornare augue at, egestas nisi. Mauris ultrices
|
|
23
|
+
orci fringilla pretium mattis. Aliquam erat volutpat. Sed pharetra condimentum dui, nec bibendum ante.
|
|
24
|
+
Vestibulum sollicitudin, sem accumsan pharetra molestie, purus turpis lacinia lorem, commodo sodales quam
|
|
25
|
+
lectus a urna. Nam gravida, felis a lacinia varius, ex ipsum ultrices orci, non egestas diam velit in mi. Ut
|
|
26
|
+
sit amet commodo orci. Duis sed diam odio. Duis mi metus, dignissim in odio nec, ornare aliquet libero. Sed
|
|
27
|
+
luctus elit nibh. Quisque et felis diam. Integer ac metus dolor.
|
|
28
|
+
</p>
|
|
29
|
+
</>
|
|
30
|
+
)
|
|
31
|
+
},
|
|
7
32
|
{ name: 'Second step', component: <p>Step 2 content</p> },
|
|
8
33
|
{ name: 'Third step', component: <p>Step 3 content</p> },
|
|
9
34
|
{ name: 'Fourth step', component: <p>Step 4 content</p> },
|
|
@@ -349,7 +349,6 @@ export const AlertGroupToastWithNotificationDrawer: React.FunctionComponent = ()
|
|
|
349
349
|
minusBtnAriaLabel="minus"
|
|
350
350
|
plusBtnAriaLabel="plus"
|
|
351
351
|
style={{ margin: '12px 0' }}
|
|
352
|
-
allowEmptyInput
|
|
353
352
|
/>
|
|
354
353
|
</PageSection>
|
|
355
354
|
<PageSection variant={PageSectionVariants.light}>
|
|
@@ -2,6 +2,9 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
|
|
4
4
|
import { css } from '@patternfly/react-styles';
|
|
5
|
+
import { WizardContext } from './WizardContext';
|
|
6
|
+
import { debounce } from '../../../helpers/util';
|
|
7
|
+
import { getResizeObserver } from '../../../helpers/resizeObserver';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Used as a wrapper for WizardStep content, where the wrapping element is customizable.
|
|
@@ -11,9 +14,13 @@ export interface WizardBodyProps {
|
|
|
11
14
|
children: React.ReactNode | React.ReactNode[];
|
|
12
15
|
/** Set to true to remove the default body padding */
|
|
13
16
|
hasNoPadding?: boolean;
|
|
14
|
-
/**
|
|
17
|
+
/** Adds an accessible name to the wrapper element when the content overflows and renders
|
|
18
|
+
* a scrollbar.
|
|
19
|
+
*/
|
|
15
20
|
'aria-label'?: string;
|
|
16
|
-
/**
|
|
21
|
+
/** Adds an accessible name to the wrapper element by passing the the id of one or more elements.
|
|
22
|
+
* The aria-labelledby will only be applied when the content overflows and renders a scrollbar.
|
|
23
|
+
*/
|
|
17
24
|
'aria-labelledby'?: string;
|
|
18
25
|
/** Component used as the wrapping content container */
|
|
19
26
|
component?: React.ElementType;
|
|
@@ -24,11 +31,53 @@ export const WizardBody = ({
|
|
|
24
31
|
hasNoPadding = false,
|
|
25
32
|
'aria-label': ariaLabel,
|
|
26
33
|
'aria-labelledby': ariaLabelledBy,
|
|
27
|
-
component
|
|
28
|
-
}: WizardBodyProps) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
component = 'div'
|
|
35
|
+
}: WizardBodyProps) => {
|
|
36
|
+
const [hasScrollbar, setHasScrollbar] = React.useState(false);
|
|
37
|
+
const [previousWidth, setPreviousWidth] = React.useState<number | undefined>(undefined);
|
|
38
|
+
const wrapperRef = React.useRef(null);
|
|
39
|
+
const WrapperComponent = component;
|
|
40
|
+
const { activeStep } = React.useContext(WizardContext);
|
|
41
|
+
const defaultAriaLabel = ariaLabel || `${activeStep?.name} content`;
|
|
42
|
+
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
const resize = () => {
|
|
45
|
+
if (wrapperRef?.current) {
|
|
46
|
+
const { offsetWidth, offsetHeight, scrollHeight } = wrapperRef.current;
|
|
47
|
+
|
|
48
|
+
if (previousWidth !== offsetWidth) {
|
|
49
|
+
setPreviousWidth(offsetWidth);
|
|
50
|
+
setHasScrollbar(offsetHeight < scrollHeight);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleResizeWithDelay = debounce(resize, 250);
|
|
56
|
+
let observer = () => {};
|
|
57
|
+
|
|
58
|
+
if (wrapperRef?.current) {
|
|
59
|
+
observer = getResizeObserver(wrapperRef.current, handleResizeWithDelay);
|
|
60
|
+
const { offsetHeight, scrollHeight } = wrapperRef.current;
|
|
61
|
+
|
|
62
|
+
setHasScrollbar(offsetHeight < scrollHeight);
|
|
63
|
+
setPreviousWidth((wrapperRef.current as HTMLElement).offsetWidth);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
observer();
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<WrapperComponent
|
|
73
|
+
ref={wrapperRef}
|
|
74
|
+
{...(component === 'div' && hasScrollbar && { role: 'region' })}
|
|
75
|
+
{...(hasScrollbar && { 'aria-label': defaultAriaLabel, 'aria-labelledby': ariaLabelledBy, tabIndex: 0 })}
|
|
76
|
+
className={css(styles.wizardMain)}
|
|
77
|
+
>
|
|
78
|
+
<div className={css(styles.wizardMainBody, hasNoPadding && styles.modifiers.noPadding)}>{children}</div>
|
|
79
|
+
</WrapperComponent>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
33
82
|
|
|
34
83
|
WizardBody.displayName = 'WizardBody';
|
|
@@ -20,16 +20,6 @@ test('has padding className when hasNoPadding is specified', () => {
|
|
|
20
20
|
expect(screen.getByText('content')).toHaveClass('pf-m-no-padding');
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
test('has aria-label when one is specified', () => {
|
|
24
|
-
render(<WizardBody aria-label="Body label">content</WizardBody>);
|
|
25
|
-
expect(screen.getByLabelText('Body label')).toBeVisible();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('has aria-labelledby when one is specified', () => {
|
|
29
|
-
const { container } = render(<WizardBody aria-labelledby="some-id">content</WizardBody>);
|
|
30
|
-
expect(container.firstElementChild).toHaveAttribute('aria-labelledby', 'some-id');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
23
|
test('wrapper element is of type div when component is not specified', () => {
|
|
34
24
|
const { container } = render(<WizardBody aria-label="Wizard body">content</WizardBody>);
|
|
35
25
|
expect(container.firstElementChild?.tagName).toEqual('DIV');
|
|
@@ -164,7 +164,7 @@ const StepContent = () => {
|
|
|
164
164
|
|
|
165
165
|
### useWizardFooter
|
|
166
166
|
|
|
167
|
-
Used to set a unique footer for the wizard on any given step.
|
|
167
|
+
Used to set a unique footer for the wizard on any given step.
|
|
168
168
|
|
|
169
169
|
```noLive
|
|
170
170
|
import { useWizardFooter } from '@patternfly/react-core/next';
|
|
@@ -4,7 +4,25 @@ import { Wizard, WizardStep } from '@patternfly/react-core/next';
|
|
|
4
4
|
export const WizardBasic: React.FunctionComponent = () => (
|
|
5
5
|
<Wizard height={400} title="Basic wizard">
|
|
6
6
|
<WizardStep name="Step 1" id="basic-first-step">
|
|
7
|
-
|
|
7
|
+
<p>
|
|
8
|
+
The content of this step overflows and creates a scrollbar, which causes a tabindex of "0", a role of "region",
|
|
9
|
+
and an aria-label or aria-labelledby to be applied.
|
|
10
|
+
</p>
|
|
11
|
+
<p>
|
|
12
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer bibendum in neque nec pharetra. Duis lacinia
|
|
13
|
+
vel sapien ut imperdiet. Nunc ultrices mollis dictum. Duis tempus, massa nec tincidunt tempor, enim ex porttitor
|
|
14
|
+
odio, eu facilisis dolor tortor id sapien. Etiam sit amet molestie lacus. Nulla facilisi. Duis eget finibus
|
|
15
|
+
ipsum. Quisque dictum enim sed sodales porta. Curabitur eget orci eu risus posuere pulvinar id nec turpis. Morbi
|
|
16
|
+
mattis orci vel posuere tincidunt. Fusce bibendum et libero a auctor.
|
|
17
|
+
</p>
|
|
18
|
+
<p>
|
|
19
|
+
Proin elementum commodo sodales. Quisque eget libero mattis, ornare augue at, egestas nisi. Mauris ultrices orci
|
|
20
|
+
fringilla pretium mattis. Aliquam erat volutpat. Sed pharetra condimentum dui, nec bibendum ante. Vestibulum
|
|
21
|
+
sollicitudin, sem accumsan pharetra molestie, purus turpis lacinia lorem, commodo sodales quam lectus a urna.
|
|
22
|
+
Nam gravida, felis a lacinia varius, ex ipsum ultrices orci, non egestas diam velit in mi. Ut sit amet commodo
|
|
23
|
+
orci. Duis sed diam odio. Duis mi metus, dignissim in odio nec, ornare aliquet libero. Sed luctus elit nibh.
|
|
24
|
+
Quisque et felis diam. Integer ac metus dolor.
|
|
25
|
+
</p>
|
|
8
26
|
</WizardStep>
|
|
9
27
|
<WizardStep name="Step 2" id="basic-second-step">
|
|
10
28
|
Step 2 content
|