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