@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.
Files changed (190) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/esm/next/components/Select/Select.d.ts +28 -0
  3. package/dist/esm/next/components/Select/Select.d.ts.map +1 -0
  4. package/dist/esm/next/components/Select/Select.js +62 -0
  5. package/dist/esm/next/components/Select/Select.js.map +1 -0
  6. package/dist/esm/next/components/Select/SelectGroup.d.ts +12 -0
  7. package/dist/esm/next/components/Select/SelectGroup.d.ts.map +1 -0
  8. package/dist/esm/next/components/Select/SelectGroup.js +10 -0
  9. package/dist/esm/next/components/Select/SelectGroup.js.map +1 -0
  10. package/dist/esm/next/components/Select/SelectList.d.ts +10 -0
  11. package/dist/esm/next/components/Select/SelectList.d.ts.map +1 -0
  12. package/dist/esm/next/components/Select/SelectList.js +10 -0
  13. package/dist/esm/next/components/Select/SelectList.js.map +1 -0
  14. package/dist/esm/next/components/Select/SelectOption.d.ts +20 -0
  15. package/dist/esm/next/components/Select/SelectOption.d.ts.map +1 -0
  16. package/dist/esm/next/components/Select/SelectOption.js +10 -0
  17. package/dist/esm/next/components/Select/SelectOption.js.map +1 -0
  18. package/dist/esm/next/components/Select/index.d.ts +5 -0
  19. package/dist/esm/next/components/Select/index.d.ts.map +1 -0
  20. package/dist/esm/next/components/Select/index.js +5 -0
  21. package/dist/esm/next/components/Select/index.js.map +1 -0
  22. package/dist/esm/next/components/Wizard/Wizard.d.ts +9 -7
  23. package/dist/esm/next/components/Wizard/Wizard.d.ts.map +1 -1
  24. package/dist/esm/next/components/Wizard/Wizard.js +62 -48
  25. package/dist/esm/next/components/Wizard/Wizard.js.map +1 -1
  26. package/dist/esm/next/components/Wizard/WizardBody.d.ts +2 -2
  27. package/dist/esm/next/components/Wizard/WizardBody.d.ts.map +1 -1
  28. package/dist/esm/next/components/Wizard/WizardBody.js +1 -1
  29. package/dist/esm/next/components/Wizard/WizardBody.js.map +1 -1
  30. package/dist/esm/next/components/Wizard/WizardContext.d.ts +21 -20
  31. package/dist/esm/next/components/Wizard/WizardContext.d.ts.map +1 -1
  32. package/dist/esm/next/components/Wizard/WizardContext.js +63 -32
  33. package/dist/esm/next/components/Wizard/WizardContext.js.map +1 -1
  34. package/dist/esm/next/components/Wizard/WizardFooter.d.ts +20 -6
  35. package/dist/esm/next/components/Wizard/WizardFooter.d.ts.map +1 -1
  36. package/dist/esm/next/components/Wizard/WizardFooter.js +13 -4
  37. package/dist/esm/next/components/Wizard/WizardFooter.js.map +1 -1
  38. package/dist/esm/next/components/Wizard/WizardHeader.d.ts +1 -1
  39. package/dist/esm/next/components/Wizard/WizardHeader.d.ts.map +1 -1
  40. package/dist/esm/next/components/Wizard/WizardHeader.js +2 -2
  41. package/dist/esm/next/components/Wizard/WizardHeader.js.map +1 -1
  42. package/dist/esm/next/components/Wizard/WizardNav.d.ts +5 -5
  43. package/dist/esm/next/components/Wizard/WizardNav.d.ts.map +1 -1
  44. package/dist/esm/next/components/Wizard/WizardNav.js +3 -4
  45. package/dist/esm/next/components/Wizard/WizardNav.js.map +1 -1
  46. package/dist/esm/next/components/Wizard/WizardNavInternal.d.ts +14 -0
  47. package/dist/esm/next/components/Wizard/WizardNavInternal.d.ts.map +1 -0
  48. package/dist/esm/next/components/Wizard/WizardNavInternal.js +49 -0
  49. package/dist/esm/next/components/Wizard/WizardNavInternal.js.map +1 -0
  50. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts +12 -8
  51. package/dist/esm/next/components/Wizard/WizardNavItem.d.ts.map +1 -1
  52. package/dist/esm/next/components/Wizard/WizardNavItem.js +23 -12
  53. package/dist/esm/next/components/Wizard/WizardNavItem.js.map +1 -1
  54. package/dist/esm/next/components/Wizard/WizardStep.d.ts +22 -6
  55. package/dist/esm/next/components/Wizard/WizardStep.d.ts.map +1 -1
  56. package/dist/esm/next/components/Wizard/WizardStep.js +20 -1
  57. package/dist/esm/next/components/Wizard/WizardStep.js.map +1 -1
  58. package/dist/esm/next/components/Wizard/WizardToggle.d.ts +9 -15
  59. package/dist/esm/next/components/Wizard/WizardToggle.d.ts.map +1 -1
  60. package/dist/esm/next/components/Wizard/WizardToggle.js +15 -50
  61. package/dist/esm/next/components/Wizard/WizardToggle.js.map +1 -1
  62. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts +2 -2
  63. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -1
  64. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js +3 -3
  65. package/dist/esm/next/components/Wizard/hooks/useWizardFooter.js.map +1 -1
  66. package/dist/esm/next/components/Wizard/types.d.ts +37 -46
  67. package/dist/esm/next/components/Wizard/types.d.ts.map +1 -1
  68. package/dist/esm/next/components/Wizard/types.js +10 -2
  69. package/dist/esm/next/components/Wizard/types.js.map +1 -1
  70. package/dist/esm/next/components/Wizard/utils.d.ts +4 -5
  71. package/dist/esm/next/components/Wizard/utils.d.ts.map +1 -1
  72. package/dist/esm/next/components/Wizard/utils.js +20 -13
  73. package/dist/esm/next/components/Wizard/utils.js.map +1 -1
  74. package/dist/esm/next/components/index.d.ts +2 -1
  75. package/dist/esm/next/components/index.d.ts.map +1 -1
  76. package/dist/esm/next/components/index.js +2 -1
  77. package/dist/esm/next/components/index.js.map +1 -1
  78. package/dist/js/next/components/Select/Select.d.ts +28 -0
  79. package/dist/js/next/components/Select/Select.d.ts.map +1 -0
  80. package/dist/js/next/components/Select/Select.js +65 -0
  81. package/dist/js/next/components/Select/Select.js.map +1 -0
  82. package/dist/js/next/components/Select/SelectGroup.d.ts +12 -0
  83. package/dist/js/next/components/Select/SelectGroup.d.ts.map +1 -0
  84. package/dist/js/next/components/Select/SelectGroup.js +14 -0
  85. package/dist/js/next/components/Select/SelectGroup.js.map +1 -0
  86. package/dist/js/next/components/Select/SelectList.d.ts +10 -0
  87. package/dist/js/next/components/Select/SelectList.d.ts.map +1 -0
  88. package/dist/js/next/components/Select/SelectList.js +14 -0
  89. package/dist/js/next/components/Select/SelectList.js.map +1 -0
  90. package/dist/js/next/components/Select/SelectOption.d.ts +20 -0
  91. package/dist/js/next/components/Select/SelectOption.d.ts.map +1 -0
  92. package/dist/js/next/components/Select/SelectOption.js +14 -0
  93. package/dist/js/next/components/Select/SelectOption.js.map +1 -0
  94. package/dist/js/next/components/Select/index.d.ts +5 -0
  95. package/dist/js/next/components/Select/index.d.ts.map +1 -0
  96. package/dist/js/next/components/Select/index.js +8 -0
  97. package/dist/js/next/components/Select/index.js.map +1 -0
  98. package/dist/js/next/components/Wizard/Wizard.d.ts +9 -7
  99. package/dist/js/next/components/Wizard/Wizard.d.ts.map +1 -1
  100. package/dist/js/next/components/Wizard/Wizard.js +59 -45
  101. package/dist/js/next/components/Wizard/Wizard.js.map +1 -1
  102. package/dist/js/next/components/Wizard/WizardBody.d.ts +2 -2
  103. package/dist/js/next/components/Wizard/WizardBody.d.ts.map +1 -1
  104. package/dist/js/next/components/Wizard/WizardBody.js +1 -1
  105. package/dist/js/next/components/Wizard/WizardBody.js.map +1 -1
  106. package/dist/js/next/components/Wizard/WizardContext.d.ts +21 -20
  107. package/dist/js/next/components/Wizard/WizardContext.d.ts.map +1 -1
  108. package/dist/js/next/components/Wizard/WizardContext.js +61 -30
  109. package/dist/js/next/components/Wizard/WizardContext.js.map +1 -1
  110. package/dist/js/next/components/Wizard/WizardFooter.d.ts +20 -6
  111. package/dist/js/next/components/Wizard/WizardFooter.d.ts.map +1 -1
  112. package/dist/js/next/components/Wizard/WizardFooter.js +15 -6
  113. package/dist/js/next/components/Wizard/WizardFooter.js.map +1 -1
  114. package/dist/js/next/components/Wizard/WizardHeader.d.ts +1 -1
  115. package/dist/js/next/components/Wizard/WizardHeader.d.ts.map +1 -1
  116. package/dist/js/next/components/Wizard/WizardHeader.js +2 -2
  117. package/dist/js/next/components/Wizard/WizardHeader.js.map +1 -1
  118. package/dist/js/next/components/Wizard/WizardNav.d.ts +5 -5
  119. package/dist/js/next/components/Wizard/WizardNav.d.ts.map +1 -1
  120. package/dist/js/next/components/Wizard/WizardNav.js +3 -4
  121. package/dist/js/next/components/Wizard/WizardNav.js.map +1 -1
  122. package/dist/js/next/components/Wizard/WizardNavInternal.d.ts +14 -0
  123. package/dist/js/next/components/Wizard/WizardNavInternal.d.ts.map +1 -0
  124. package/dist/js/next/components/Wizard/WizardNavInternal.js +54 -0
  125. package/dist/js/next/components/Wizard/WizardNavInternal.js.map +1 -0
  126. package/dist/js/next/components/Wizard/WizardNavItem.d.ts +12 -8
  127. package/dist/js/next/components/Wizard/WizardNavItem.d.ts.map +1 -1
  128. package/dist/js/next/components/Wizard/WizardNavItem.js +23 -12
  129. package/dist/js/next/components/Wizard/WizardNavItem.js.map +1 -1
  130. package/dist/js/next/components/Wizard/WizardStep.d.ts +22 -6
  131. package/dist/js/next/components/Wizard/WizardStep.d.ts.map +1 -1
  132. package/dist/js/next/components/Wizard/WizardStep.js +20 -1
  133. package/dist/js/next/components/Wizard/WizardStep.js.map +1 -1
  134. package/dist/js/next/components/Wizard/WizardToggle.d.ts +9 -15
  135. package/dist/js/next/components/Wizard/WizardToggle.d.ts.map +1 -1
  136. package/dist/js/next/components/Wizard/WizardToggle.js +15 -51
  137. package/dist/js/next/components/Wizard/WizardToggle.js.map +1 -1
  138. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts +2 -2
  139. package/dist/js/next/components/Wizard/hooks/useWizardFooter.d.ts.map +1 -1
  140. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js +3 -3
  141. package/dist/js/next/components/Wizard/hooks/useWizardFooter.js.map +1 -1
  142. package/dist/js/next/components/Wizard/types.d.ts +37 -46
  143. package/dist/js/next/components/Wizard/types.d.ts.map +1 -1
  144. package/dist/js/next/components/Wizard/types.js +12 -3
  145. package/dist/js/next/components/Wizard/types.js.map +1 -1
  146. package/dist/js/next/components/Wizard/utils.d.ts +4 -5
  147. package/dist/js/next/components/Wizard/utils.d.ts.map +1 -1
  148. package/dist/js/next/components/Wizard/utils.js +24 -15
  149. package/dist/js/next/components/Wizard/utils.js.map +1 -1
  150. package/dist/js/next/components/index.d.ts +2 -1
  151. package/dist/js/next/components/index.d.ts.map +1 -1
  152. package/dist/js/next/components/index.js +2 -1
  153. package/dist/js/next/components/index.js.map +1 -1
  154. package/dist/umd/react-core.min.css +940 -940
  155. package/package.json +2 -2
  156. package/src/next/components/Select/Select.tsx +129 -0
  157. package/src/next/components/Select/SelectGroup.tsx +24 -0
  158. package/src/next/components/Select/SelectList.tsx +21 -0
  159. package/src/next/components/Select/SelectOption.tsx +31 -0
  160. package/src/next/components/Select/examples/Select.md +37 -0
  161. package/src/next/components/Select/examples/SelectBasic.tsx +54 -0
  162. package/src/next/components/Select/examples/SelectCheckbox.tsx +67 -0
  163. package/src/next/components/Select/examples/SelectGrouped.tsx +63 -0
  164. package/src/next/components/Select/examples/SelectMultiTypeahead.tsx +195 -0
  165. package/src/next/components/Select/examples/SelectTypeahead.tsx +191 -0
  166. package/src/next/components/Select/index.ts +4 -0
  167. package/src/next/components/Wizard/Wizard.tsx +107 -64
  168. package/src/next/components/Wizard/WizardBody.tsx +4 -4
  169. package/src/next/components/Wizard/WizardContext.tsx +117 -65
  170. package/src/next/components/Wizard/WizardFooter.tsx +39 -17
  171. package/src/next/components/Wizard/WizardHeader.tsx +6 -3
  172. package/src/next/components/Wizard/WizardNav.tsx +9 -10
  173. package/src/next/components/Wizard/WizardNavInternal.tsx +137 -0
  174. package/src/next/components/Wizard/WizardNavItem.tsx +47 -20
  175. package/src/next/components/Wizard/WizardStep.tsx +52 -7
  176. package/src/next/components/Wizard/WizardToggle.tsx +24 -142
  177. package/src/next/components/Wizard/__tests__/Wizard.test.tsx +10 -13
  178. package/src/next/components/Wizard/__tests__/WizardBody.test.tsx +3 -3
  179. package/src/next/components/Wizard/__tests__/WizardFooter.test.tsx +11 -14
  180. package/src/next/components/Wizard/__tests__/WizardStep.test.tsx +100 -0
  181. package/src/next/components/Wizard/__tests__/WizardToggle.test.tsx +4 -4
  182. package/src/next/components/Wizard/examples/Wizard.md +40 -15
  183. package/src/next/components/Wizard/examples/WizardBasic.tsx +2 -2
  184. package/src/next/components/Wizard/examples/WizardCustomNav.tsx +4 -4
  185. package/src/next/components/Wizard/examples/WizardKitchenSink.tsx +126 -60
  186. package/src/next/components/Wizard/hooks/__tests__/useWizardFooter.test.tsx +23 -34
  187. package/src/next/components/Wizard/hooks/useWizardFooter.tsx +5 -5
  188. package/src/next/components/Wizard/types.tsx +59 -53
  189. package/src/next/components/Wizard/utils.ts +25 -16
  190. package/src/next/components/index.ts +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.249.0",
3
+ "version": "4.250.0",
4
4
  "description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -70,5 +70,5 @@
70
70
  "react": "^16.8 || ^17 || ^18",
71
71
  "react-dom": "^16.8 || ^17 || ^18"
72
72
  },
73
- "gitHead": "af40c12e9dc55ed508ebc5e07415ec93d61bd639"
73
+ "gitHead": "50c13d9abd3516bedbf2dbef4390784ef25cf035"
74
74
  }
@@ -0,0 +1,129 @@
1
+ import React from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+ import { Menu, MenuContent, MenuProps } from '../../../components/Menu';
4
+ import { Popper } from '../../../helpers/Popper/Popper';
5
+ import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../../helpers';
6
+
7
+ export interface SelectProps extends MenuProps, OUIAProps {
8
+ /** Anything which can be rendered in a select */
9
+ children?: React.ReactNode;
10
+ /** Classes applied to root element of select */
11
+ className?: string;
12
+ /** Flag to indicate if select is open */
13
+ isOpen?: boolean;
14
+ /** Single itemId for single select menus, or array of itemIds for multi select. You can also specify isSelected on the SelectOption. */
15
+ selected?: any | any[];
16
+ /** Renderer for a custom select toggle. Forwards a ref to the toggle. */
17
+ toggle: (toggleRef: React.RefObject<any>) => React.ReactNode;
18
+ /** Function callback when user selects an option. */
19
+ onSelect?: (event?: React.MouseEvent<Element, MouseEvent>, itemId?: string | number) => void;
20
+ /** Callback to allow the select component to change the open state of the menu.
21
+ * Triggered by clicking outside of the menu, or by pressing either tab or escape. */
22
+ onOpenChange?: (isOpen: boolean) => void;
23
+ /** Indicates if the select should be without the outer box-shadow */
24
+ isPlain?: boolean;
25
+ /** Minimum width of the select menu */
26
+ minWidth?: string;
27
+ /** @hide Forwarded ref */
28
+ innerRef?: React.Ref<HTMLDivElement>;
29
+ }
30
+
31
+ const SelectBase: React.FunctionComponent<SelectProps & OUIAProps> = ({
32
+ children,
33
+ className,
34
+ onSelect,
35
+ isOpen,
36
+ selected,
37
+ toggle,
38
+ onOpenChange,
39
+ isPlain,
40
+ minWidth,
41
+ innerRef,
42
+ ...props
43
+ }: SelectProps & OUIAProps) => {
44
+ const localMenuRef = React.useRef<HTMLDivElement>();
45
+ const toggleRef = React.useRef<HTMLButtonElement>();
46
+ const containerRef = React.useRef<HTMLDivElement>();
47
+
48
+ const menuRef = (innerRef as React.RefObject<HTMLDivElement>) || localMenuRef;
49
+ React.useEffect(() => {
50
+ const handleMenuKeys = (event: KeyboardEvent) => {
51
+ if (!isOpen && toggleRef.current?.contains(event.target as Node)) {
52
+ // toggle was clicked open, focus on first menu item
53
+ if (event.key === 'Enter' || event.key === 'Space') {
54
+ setTimeout(() => {
55
+ const firstElement = menuRef?.current?.querySelector('li button:not(:disabled),li input:not(:disabled)');
56
+ firstElement && (firstElement as HTMLElement).focus();
57
+ }, 0);
58
+ }
59
+ }
60
+ // Close the menu on tab or escape if onOpenChange is provided
61
+ if (
62
+ (isOpen && onOpenChange && menuRef.current?.contains(event.target as Node)) ||
63
+ toggleRef.current?.contains(event.target as Node)
64
+ ) {
65
+ if (event.key === 'Escape' || event.key === 'Tab') {
66
+ onOpenChange(!isOpen);
67
+ toggleRef.current?.focus();
68
+ }
69
+ }
70
+ };
71
+
72
+ const handleClickOutside = (event: MouseEvent) => {
73
+ // If the event is not on the toggle and onOpenChange callback is provided, close the menu
74
+ if (isOpen && onOpenChange && !toggleRef?.current?.contains(event.target as Node)) {
75
+ if (isOpen && !menuRef.current?.contains(event.target as Node)) {
76
+ onOpenChange(false);
77
+ }
78
+ }
79
+ };
80
+
81
+ window.addEventListener('keydown', handleMenuKeys);
82
+ window.addEventListener('click', handleClickOutside);
83
+
84
+ return () => {
85
+ window.removeEventListener('keydown', handleMenuKeys);
86
+ window.removeEventListener('click', handleClickOutside);
87
+ };
88
+ }, [isOpen, menuRef, onOpenChange]);
89
+
90
+ const menu = (
91
+ <Menu
92
+ className={css(className)}
93
+ ref={menuRef}
94
+ onSelect={(event, itemId) => onSelect(event, itemId)}
95
+ isPlain={isPlain}
96
+ selected={selected}
97
+ {...(minWidth && {
98
+ style: {
99
+ '--pf-c-menu--MinWidth': minWidth
100
+ } as React.CSSProperties
101
+ })}
102
+ {...getOUIAProps(
103
+ Select.displayName,
104
+ props.ouiaId !== undefined ? props.ouiaId : getDefaultOUIAId(Select.displayName),
105
+ props.ouiaSafe !== undefined ? props.ouiaSafe : true
106
+ )}
107
+ {...props}
108
+ >
109
+ <MenuContent>{children}</MenuContent>
110
+ </Menu>
111
+ );
112
+ return (
113
+ <div ref={containerRef}>
114
+ <Popper
115
+ trigger={toggle(toggleRef)}
116
+ removeFindDomNode
117
+ popper={menu}
118
+ appendTo={containerRef.current || undefined}
119
+ isVisible={isOpen}
120
+ />
121
+ </div>
122
+ );
123
+ };
124
+
125
+ export const Select = React.forwardRef((props: SelectProps, ref: React.Ref<any>) => (
126
+ <SelectBase innerRef={ref} {...props} />
127
+ ));
128
+
129
+ Select.displayName = 'Select';
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+ import { MenuGroupProps, MenuGroup } from '../../../components/Menu';
4
+
5
+ export interface SelectGroupProps extends Omit<MenuGroupProps, 'ref'> {
6
+ /** Anything which can be rendered in a select group */
7
+ children: React.ReactNode;
8
+ /** Classes applied to root element of select group */
9
+ className?: string;
10
+ /** Label of the select group */
11
+ label?: string;
12
+ }
13
+
14
+ export const SelectGroup: React.FunctionComponent<SelectGroupProps> = ({
15
+ children,
16
+ className,
17
+ label,
18
+ ...props
19
+ }: SelectGroupProps) => (
20
+ <MenuGroup className={css(className)} label={label} {...props}>
21
+ {children}
22
+ </MenuGroup>
23
+ );
24
+ SelectGroup.displayName = 'SelectGroup';
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+ import { MenuListProps, MenuList } from '../../../components/Menu';
4
+
5
+ export interface SelectListProps extends MenuListProps {
6
+ /** Anything which can be rendered in a select list */
7
+ children: React.ReactNode;
8
+ /** Classes applied to root element of select list */
9
+ className?: string;
10
+ }
11
+
12
+ export const SelectList: React.FunctionComponent<MenuListProps> = ({
13
+ children,
14
+ className,
15
+ ...props
16
+ }: SelectListProps) => (
17
+ <MenuList className={css(className)} {...props}>
18
+ {children}
19
+ </MenuList>
20
+ );
21
+ SelectList.displayName = 'SelectList';
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+ import { MenuItemProps, MenuItem } from '../../../components/Menu';
4
+
5
+ export interface SelectOptionProps extends Omit<MenuItemProps, 'ref'> {
6
+ /** Anything which can be rendered in a select option */
7
+ children?: React.ReactNode;
8
+ /** Classes applied to root element of select option */
9
+ className?: string;
10
+ /** Identifies the component in the Select onSelect callback */
11
+ itemId?: any;
12
+ /** Indicates the option has a checkbox */
13
+ hasCheck?: boolean;
14
+ /** Indicates the option is disabled */
15
+ isDisabled?: boolean;
16
+ /** Indicates the option is selected */
17
+ isSelected?: boolean;
18
+ /** Indicates the option is focused */
19
+ isFocused?: boolean;
20
+ }
21
+
22
+ export const SelectOption: React.FunctionComponent<MenuItemProps> = ({
23
+ children,
24
+ className,
25
+ ...props
26
+ }: SelectOptionProps) => (
27
+ <MenuItem className={css(className)} {...props}>
28
+ {children}
29
+ </MenuItem>
30
+ );
31
+ SelectOption.displayName = 'SelectOption';
@@ -0,0 +1,37 @@
1
+ ---
2
+ id: Select
3
+ section: components
4
+ cssPrefix: pf-c-menu
5
+ propComponents: ['Select', SelectGroup, 'SelectOption', 'SelectList']
6
+ beta: true
7
+ ouia: true
8
+ ---
9
+
10
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
11
+
12
+ ## Examples
13
+
14
+ ### Single
15
+
16
+ ```ts file="./SelectBasic.tsx"
17
+ ```
18
+
19
+ ### Grouped single
20
+
21
+ ```ts file="./SelectGrouped.tsx"
22
+ ```
23
+
24
+ ### Checkbox
25
+
26
+ ```ts file="./SelectCheckbox.tsx"
27
+ ```
28
+
29
+ ### Typeahead
30
+
31
+ ```ts file="./SelectTypeahead.tsx"
32
+ ```
33
+
34
+ ### Multiple Typeahead
35
+
36
+ ```ts file="./SelectMultiTypeahead.tsx"
37
+ ```
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { Select, SelectOption, SelectList } from '@patternfly/react-core/next';
3
+ import { MenuToggle, MenuToggleElement } from '@patternfly/react-core';
4
+
5
+ export const SelectBasic: React.FunctionComponent = () => {
6
+ const [isOpen, setIsOpen] = React.useState(false);
7
+ const [selected, setSelected] = React.useState<string>('Select a value');
8
+ const menuRef = React.useRef<HTMLDivElement>(null);
9
+
10
+ const onToggleClick = () => {
11
+ setIsOpen(!isOpen);
12
+ };
13
+
14
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
15
+ // eslint-disable-next-line no-console
16
+ console.log('selected', itemId);
17
+
18
+ setSelected(itemId as string);
19
+ setIsOpen(false);
20
+ };
21
+
22
+ const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
23
+ <MenuToggle
24
+ ref={toggleRef}
25
+ onClick={onToggleClick}
26
+ isExpanded={isOpen}
27
+ style={
28
+ {
29
+ width: '200px'
30
+ } as React.CSSProperties
31
+ }
32
+ >
33
+ {selected}
34
+ </MenuToggle>
35
+ );
36
+
37
+ return (
38
+ <Select
39
+ id="single-select"
40
+ ref={menuRef}
41
+ isOpen={isOpen}
42
+ selected={selected}
43
+ onSelect={onSelect}
44
+ onOpenChange={isOpen => setIsOpen(isOpen)}
45
+ toggle={toggle}
46
+ >
47
+ <SelectList>
48
+ <SelectOption itemId="Option 1">Option 1</SelectOption>
49
+ <SelectOption itemId="Option 2">Option 2</SelectOption>
50
+ <SelectOption itemId="Option 3">Option 3</SelectOption>
51
+ </SelectList>
52
+ </Select>
53
+ );
54
+ };
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import { Select, SelectOption, SelectList } from '@patternfly/react-core/next';
3
+ import { MenuToggle, MenuToggleElement, Badge } from '@patternfly/react-core';
4
+
5
+ export const SelectCheckbox: React.FunctionComponent = () => {
6
+ const [isOpen, setIsOpen] = React.useState(false);
7
+ const [selectedItems, setSelectedItems] = React.useState<number[]>([]);
8
+ const menuRef = React.useRef<HTMLDivElement>(null);
9
+
10
+ const onToggleClick = () => {
11
+ setIsOpen(!isOpen);
12
+ };
13
+
14
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
15
+ // eslint-disable-next-line no-console
16
+ console.log('selected', itemId);
17
+
18
+ if (selectedItems.includes(itemId as number)) {
19
+ setSelectedItems(selectedItems.filter(id => id !== itemId));
20
+ } else {
21
+ setSelectedItems([...selectedItems, itemId as number]);
22
+ }
23
+ };
24
+
25
+ const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
26
+ <MenuToggle
27
+ ref={toggleRef}
28
+ onClick={onToggleClick}
29
+ isExpanded={isOpen}
30
+ style={
31
+ {
32
+ width: '200px'
33
+ } as React.CSSProperties
34
+ }
35
+ >
36
+ Filter by status
37
+ {selectedItems.length > 0 && <Badge isRead>{selectedItems.length}</Badge>}
38
+ </MenuToggle>
39
+ );
40
+
41
+ return (
42
+ <Select
43
+ id="checkbox-select"
44
+ ref={menuRef}
45
+ isOpen={isOpen}
46
+ selected={selectedItems}
47
+ onSelect={onSelect}
48
+ onOpenChange={(nextOpen: boolean) => setIsOpen(nextOpen)}
49
+ toggle={toggle}
50
+ >
51
+ <SelectList>
52
+ <SelectOption hasCheck itemId={0} isSelected={selectedItems.includes(0)}>
53
+ Debug
54
+ </SelectOption>
55
+ <SelectOption hasCheck itemId={1} isSelected={selectedItems.includes(1)}>
56
+ Info
57
+ </SelectOption>
58
+ <SelectOption hasCheck itemId={2} isSelected={selectedItems.includes(2)}>
59
+ Warn
60
+ </SelectOption>
61
+ <SelectOption hasCheck isDisabled itemId={4} isSelected={selectedItems.includes(4)}>
62
+ Error
63
+ </SelectOption>
64
+ </SelectList>
65
+ </Select>
66
+ );
67
+ };
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import { Select, SelectOption, SelectList, SelectGroup } from '@patternfly/react-core/next';
3
+ import { MenuToggle, MenuToggleElement } from '@patternfly/react-core';
4
+
5
+ export const SelectBasic: React.FunctionComponent = () => {
6
+ const [isOpen, setIsOpen] = React.useState(false);
7
+ const [selected, setSelected] = React.useState<string>('Select a value');
8
+ const menuRef = React.useRef<HTMLDivElement>(null);
9
+
10
+ const onToggleClick = () => {
11
+ setIsOpen(!isOpen);
12
+ };
13
+
14
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
15
+ // eslint-disable-next-line no-console
16
+ console.log('selected', itemId);
17
+
18
+ setSelected(itemId as string);
19
+ setIsOpen(false);
20
+ };
21
+
22
+ const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
23
+ <MenuToggle
24
+ ref={toggleRef}
25
+ onClick={onToggleClick}
26
+ isExpanded={isOpen}
27
+ style={
28
+ {
29
+ width: '200px'
30
+ } as React.CSSProperties
31
+ }
32
+ >
33
+ {selected}
34
+ </MenuToggle>
35
+ );
36
+
37
+ return (
38
+ <Select
39
+ id="single-select"
40
+ ref={menuRef}
41
+ isOpen={isOpen}
42
+ selected={selected}
43
+ onSelect={onSelect}
44
+ onOpenChange={isOpen => setIsOpen(isOpen)}
45
+ toggle={toggle}
46
+ >
47
+ <SelectGroup label="Group 1">
48
+ <SelectList>
49
+ <SelectOption itemId="Option 1">Option 1</SelectOption>
50
+ <SelectOption itemId="Option 2">Option 2</SelectOption>
51
+ <SelectOption itemId="Option 3">Option 3</SelectOption>
52
+ </SelectList>
53
+ </SelectGroup>
54
+ <SelectGroup label="Group 2">
55
+ <SelectList>
56
+ <SelectOption itemId="Option 4">Option 4</SelectOption>
57
+ <SelectOption itemId="Option 5">Option 5</SelectOption>
58
+ <SelectOption itemId="Option 6">Option 6</SelectOption>
59
+ </SelectList>
60
+ </SelectGroup>
61
+ </Select>
62
+ );
63
+ };
@@ -0,0 +1,195 @@
1
+ import React from 'react';
2
+ import { Select, SelectOption, SelectList, SelectOptionProps } from '@patternfly/react-core/next';
3
+ import {
4
+ MenuToggle,
5
+ MenuToggleElement,
6
+ TextInputGroup,
7
+ TextInputGroupMain,
8
+ TextInputGroupUtilities,
9
+ ChipGroup,
10
+ Chip,
11
+ Button
12
+ } from '@patternfly/react-core';
13
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
14
+
15
+ const initialSelectOptions: SelectOptionProps[] = [
16
+ { itemId: 'Alabama', children: 'Alabama' },
17
+ { itemId: 'Florida', children: 'Florida' },
18
+ { itemId: 'New Jersey', children: 'New Jersey' },
19
+ { itemId: 'New Mexico', children: 'New Mexico' },
20
+ { itemId: 'New York', children: 'New York' },
21
+ { itemId: 'North Carolina', children: 'North Carolina' }
22
+ ];
23
+
24
+ export const SelectMultiTypeahead: React.FunctionComponent = () => {
25
+ const [isOpen, setIsOpen] = React.useState(false);
26
+ const [inputValue, setInputValue] = React.useState<string>('');
27
+ const [selected, setSelected] = React.useState<string[]>([]);
28
+ const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
29
+ const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
30
+
31
+ const menuRef = React.useRef<HTMLDivElement>(null);
32
+ const textInputRef = React.useRef<HTMLInputElement>();
33
+
34
+ React.useEffect(() => {
35
+ let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
36
+
37
+ // Filter menu items based on the text input value when one exists
38
+ if (inputValue) {
39
+ newSelectOptions = initialSelectOptions.filter(menuItem =>
40
+ String(menuItem.children)
41
+ .toLowerCase()
42
+ .includes(inputValue.toLowerCase())
43
+ );
44
+
45
+ // When no options are found after filtering, display 'No results found'
46
+ if (!newSelectOptions.length) {
47
+ newSelectOptions = [{ isDisabled: true, children: 'No results found' }];
48
+ }
49
+ }
50
+
51
+ setSelectOptions(newSelectOptions);
52
+ setFocusedItemIndex(0);
53
+ }, [inputValue]);
54
+
55
+ const handleMenuArrowKeys = (key: string) => {
56
+ let indexToFocus;
57
+
58
+ if (isOpen) {
59
+ if (key === 'ArrowUp') {
60
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
61
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
62
+ indexToFocus = selectOptions.length - 1;
63
+ } else {
64
+ indexToFocus = focusedItemIndex - 1;
65
+ }
66
+ }
67
+
68
+ if (key === 'ArrowDown') {
69
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
70
+ if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
71
+ indexToFocus = 0;
72
+ } else {
73
+ indexToFocus = focusedItemIndex + 1;
74
+ }
75
+ }
76
+
77
+ setFocusedItemIndex(indexToFocus);
78
+ }
79
+ };
80
+
81
+ const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
82
+ const enabledMenuItems = selectOptions.filter(menuItem => !menuItem.isDisabled);
83
+ const [firstMenuItem] = enabledMenuItems;
84
+ const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
85
+
86
+ switch (event.key) {
87
+ // Select the first available option
88
+ case 'Enter':
89
+ if (!isOpen) {
90
+ setIsOpen(prevIsOpen => !prevIsOpen);
91
+ } else {
92
+ onSelect(focusedItem.itemId as string);
93
+ }
94
+ break;
95
+ case 'Tab':
96
+ case 'Escape':
97
+ setIsOpen(false);
98
+ break;
99
+ case 'ArrowUp':
100
+ case 'ArrowDown':
101
+ handleMenuArrowKeys(event.key);
102
+ break;
103
+ default:
104
+ !isOpen && setIsOpen(true);
105
+ }
106
+ };
107
+
108
+ const onToggleClick = () => {
109
+ setIsOpen(!isOpen);
110
+ };
111
+
112
+ const onTextInputChange = (value: string) => {
113
+ setInputValue(value);
114
+ };
115
+
116
+ const onSelect = (itemId: string) => {
117
+ // eslint-disable-next-line no-console
118
+ console.log('selected', itemId);
119
+
120
+ if (itemId) {
121
+ setSelected(
122
+ selected.includes(itemId) ? selected.filter(selection => selection !== itemId) : [...selected, itemId]
123
+ );
124
+ }
125
+ };
126
+
127
+ const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
128
+ <MenuToggle variant="typeahead" onClick={onToggleClick} innerRef={toggleRef} isExpanded={isOpen} isFullWidth>
129
+ <TextInputGroup isPlain>
130
+ <TextInputGroupMain
131
+ value={inputValue}
132
+ onClick={onToggleClick}
133
+ onChange={onTextInputChange}
134
+ onKeyDown={onInputKeyDown}
135
+ id="multi-typeahead-select-input"
136
+ autoComplete="off"
137
+ innerRef={textInputRef}
138
+ placeholder="Select a state"
139
+ >
140
+ <ChipGroup>
141
+ {selected.map((selection, index) => (
142
+ <Chip
143
+ key={index}
144
+ onClick={ev => {
145
+ ev.stopPropagation();
146
+ onSelect(selection);
147
+ }}
148
+ >
149
+ {selection}
150
+ </Chip>
151
+ ))}
152
+ </ChipGroup>
153
+ </TextInputGroupMain>
154
+ <TextInputGroupUtilities>
155
+ {selected.length > 0 && (
156
+ <Button
157
+ variant="plain"
158
+ onClick={() => {
159
+ setInputValue('');
160
+ setSelected([]);
161
+ }}
162
+ aria-label="Clear input value"
163
+ >
164
+ <TimesIcon aria-hidden />
165
+ </Button>
166
+ )}
167
+ </TextInputGroupUtilities>
168
+ </TextInputGroup>
169
+ </MenuToggle>
170
+ );
171
+
172
+ return (
173
+ <Select
174
+ id="multi-typeahead-select"
175
+ ref={menuRef}
176
+ isOpen={isOpen}
177
+ selected={selected}
178
+ onSelect={(ev, selection) => onSelect(selection as string)}
179
+ onOpenChange={() => setIsOpen(false)}
180
+ toggle={toggle}
181
+ >
182
+ <SelectList>
183
+ {selectOptions.map((option, index) => (
184
+ <SelectOption
185
+ key={option.itemId || option.children}
186
+ isFocused={focusedItemIndex === index}
187
+ className={option.className}
188
+ {...option}
189
+ ref={null}
190
+ />
191
+ ))}
192
+ </SelectList>
193
+ </Select>
194
+ );
195
+ };