@patternfly/react-core 5.0.0-alpha.107 → 5.0.0-alpha.108

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 (271) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/esm/components/AboutModal/package.json +1 -1
  3. package/dist/esm/components/Accordion/package.json +1 -1
  4. package/dist/esm/components/ActionList/package.json +1 -1
  5. package/dist/esm/components/Alert/package.json +1 -1
  6. package/dist/esm/components/Avatar/package.json +1 -1
  7. package/dist/esm/components/BackToTop/package.json +1 -1
  8. package/dist/esm/components/Backdrop/package.json +1 -1
  9. package/dist/esm/components/BackgroundImage/package.json +1 -1
  10. package/dist/esm/components/Badge/package.json +1 -1
  11. package/dist/esm/components/Banner/package.json +1 -1
  12. package/dist/esm/components/Brand/package.json +1 -1
  13. package/dist/esm/components/Breadcrumb/package.json +1 -1
  14. package/dist/esm/components/Button/package.json +1 -1
  15. package/dist/esm/components/CalendarMonth/package.json +1 -1
  16. package/dist/esm/components/Card/package.json +1 -1
  17. package/dist/esm/components/Checkbox/package.json +1 -1
  18. package/dist/esm/components/Chip/package.json +1 -1
  19. package/dist/esm/components/ClipboardCopy/package.json +1 -1
  20. package/dist/esm/components/CodeBlock/package.json +1 -1
  21. package/dist/esm/components/DataList/package.json +1 -1
  22. package/dist/esm/components/DatePicker/package.json +1 -1
  23. package/dist/esm/components/DescriptionList/package.json +1 -1
  24. package/dist/esm/components/Divider/package.json +1 -1
  25. package/dist/esm/components/DragDrop/package.json +1 -1
  26. package/dist/esm/components/Drawer/package.json +1 -1
  27. package/dist/esm/components/Dropdown/Dropdown.d.ts +11 -3
  28. package/dist/esm/components/Dropdown/Dropdown.d.ts.map +1 -1
  29. package/dist/esm/components/Dropdown/Dropdown.js +11 -5
  30. package/dist/esm/components/Dropdown/Dropdown.js.map +1 -1
  31. package/dist/esm/components/Dropdown/package.json +1 -1
  32. package/dist/esm/components/DualListSelector/package.json +1 -1
  33. package/dist/esm/components/EmptyState/package.json +1 -1
  34. package/dist/esm/components/ExpandableSection/package.json +1 -1
  35. package/dist/esm/components/FileUpload/package.json +1 -1
  36. package/dist/esm/components/Form/package.json +1 -1
  37. package/dist/esm/components/FormSelect/package.json +1 -1
  38. package/dist/esm/components/HelperText/package.json +1 -1
  39. package/dist/esm/components/Hint/package.json +1 -1
  40. package/dist/esm/components/Icon/package.json +1 -1
  41. package/dist/esm/components/InputGroup/package.json +1 -1
  42. package/dist/esm/components/JumpLinks/package.json +1 -1
  43. package/dist/esm/components/Label/package.json +1 -1
  44. package/dist/esm/components/List/package.json +1 -1
  45. package/dist/esm/components/LoginPage/package.json +1 -1
  46. package/dist/esm/components/Masthead/package.json +1 -1
  47. package/dist/esm/components/Menu/package.json +1 -1
  48. package/dist/esm/components/MenuToggle/package.json +1 -1
  49. package/dist/esm/components/Modal/package.json +1 -1
  50. package/dist/esm/components/MultipleFileUpload/package.json +1 -1
  51. package/dist/esm/components/Nav/package.json +1 -1
  52. package/dist/esm/components/NotificationBadge/package.json +1 -1
  53. package/dist/esm/components/NotificationDrawer/package.json +1 -1
  54. package/dist/esm/components/NumberInput/package.json +1 -1
  55. package/dist/esm/components/OverflowMenu/package.json +1 -1
  56. package/dist/esm/components/Page/package.json +1 -1
  57. package/dist/esm/components/Pagination/package.json +1 -1
  58. package/dist/esm/components/Panel/package.json +1 -1
  59. package/dist/esm/components/Popover/package.json +1 -1
  60. package/dist/esm/components/Progress/package.json +1 -1
  61. package/dist/esm/components/ProgressStepper/package.json +1 -1
  62. package/dist/esm/components/Radio/package.json +1 -1
  63. package/dist/esm/components/SearchInput/package.json +1 -1
  64. package/dist/esm/components/Select/Select.d.ts +31 -5
  65. package/dist/esm/components/Select/Select.d.ts.map +1 -1
  66. package/dist/esm/components/Select/Select.js +16 -10
  67. package/dist/esm/components/Select/Select.js.map +1 -1
  68. package/dist/esm/components/Select/SelectGroup.d.ts +3 -0
  69. package/dist/esm/components/Select/SelectGroup.d.ts.map +1 -1
  70. package/dist/esm/components/Select/SelectGroup.js.map +1 -1
  71. package/dist/esm/components/Select/SelectOption.d.ts +14 -1
  72. package/dist/esm/components/Select/SelectOption.d.ts.map +1 -1
  73. package/dist/esm/components/Select/SelectOption.js +4 -3
  74. package/dist/esm/components/Select/SelectOption.js.map +1 -1
  75. package/dist/esm/components/Select/package.json +1 -1
  76. package/dist/esm/components/Sidebar/package.json +1 -1
  77. package/dist/esm/components/SimpleList/package.json +1 -1
  78. package/dist/esm/components/Skeleton/package.json +1 -1
  79. package/dist/esm/components/SkipToContent/package.json +1 -1
  80. package/dist/esm/components/Slider/package.json +1 -1
  81. package/dist/esm/components/Spinner/package.json +1 -1
  82. package/dist/esm/components/Switch/package.json +1 -1
  83. package/dist/esm/components/Tabs/package.json +1 -1
  84. package/dist/esm/components/Text/package.json +1 -1
  85. package/dist/esm/components/TextArea/package.json +1 -1
  86. package/dist/esm/components/TextInput/package.json +1 -1
  87. package/dist/esm/components/TextInputGroup/package.json +1 -1
  88. package/dist/esm/components/Tile/package.json +1 -1
  89. package/dist/esm/components/TimePicker/package.json +1 -1
  90. package/dist/esm/components/Timestamp/package.json +1 -1
  91. package/dist/esm/components/Title/package.json +1 -1
  92. package/dist/esm/components/ToggleGroup/package.json +1 -1
  93. package/dist/esm/components/Toolbar/package.json +1 -1
  94. package/dist/esm/components/Tooltip/package.json +1 -1
  95. package/dist/esm/components/TreeView/package.json +1 -1
  96. package/dist/esm/components/Truncate/package.json +1 -1
  97. package/dist/esm/components/Wizard/hooks/package.json +1 -1
  98. package/dist/esm/components/Wizard/package.json +1 -1
  99. package/dist/esm/components/package.json +1 -1
  100. package/dist/esm/deprecated/components/ApplicationLauncher/package.json +1 -1
  101. package/dist/esm/deprecated/components/ContextSelector/package.json +1 -1
  102. package/dist/esm/deprecated/components/Dropdown/package.json +1 -1
  103. package/dist/esm/deprecated/components/OptionsMenu/package.json +1 -1
  104. package/dist/esm/deprecated/components/PageHeader/package.json +1 -1
  105. package/dist/esm/deprecated/components/Select/package.json +1 -1
  106. package/dist/esm/deprecated/components/Wizard/package.json +1 -1
  107. package/dist/esm/deprecated/components/package.json +1 -1
  108. package/dist/esm/deprecated/package.json +1 -1
  109. package/dist/esm/helpers/Popper/Popper.d.ts +1 -1
  110. package/dist/esm/helpers/Popper/Popper.d.ts.map +1 -1
  111. package/dist/esm/helpers/Popper/thirdparty/popper-core/package.json +1 -1
  112. package/dist/esm/helpers/package.json +1 -1
  113. package/dist/esm/layouts/Bullseye/package.json +1 -1
  114. package/dist/esm/layouts/Flex/package.json +1 -1
  115. package/dist/esm/layouts/Gallery/package.json +1 -1
  116. package/dist/esm/layouts/Grid/package.json +1 -1
  117. package/dist/esm/layouts/Level/package.json +1 -1
  118. package/dist/esm/layouts/Split/package.json +1 -1
  119. package/dist/esm/layouts/Stack/package.json +1 -1
  120. package/dist/esm/layouts/package.json +1 -1
  121. package/dist/esm/next/components/package.json +1 -1
  122. package/dist/esm/next/package.json +1 -1
  123. package/dist/esm/package.json +1 -1
  124. package/dist/js/components/AboutModal/package.json +1 -1
  125. package/dist/js/components/Accordion/package.json +1 -1
  126. package/dist/js/components/ActionList/package.json +1 -1
  127. package/dist/js/components/Alert/package.json +1 -1
  128. package/dist/js/components/Avatar/package.json +1 -1
  129. package/dist/js/components/BackToTop/package.json +1 -1
  130. package/dist/js/components/Backdrop/package.json +1 -1
  131. package/dist/js/components/BackgroundImage/package.json +1 -1
  132. package/dist/js/components/Badge/package.json +1 -1
  133. package/dist/js/components/Banner/package.json +1 -1
  134. package/dist/js/components/Brand/package.json +1 -1
  135. package/dist/js/components/Breadcrumb/package.json +1 -1
  136. package/dist/js/components/Button/package.json +1 -1
  137. package/dist/js/components/CalendarMonth/package.json +1 -1
  138. package/dist/js/components/Card/package.json +1 -1
  139. package/dist/js/components/Checkbox/package.json +1 -1
  140. package/dist/js/components/Chip/package.json +1 -1
  141. package/dist/js/components/ClipboardCopy/package.json +1 -1
  142. package/dist/js/components/CodeBlock/package.json +1 -1
  143. package/dist/js/components/DataList/package.json +1 -1
  144. package/dist/js/components/DatePicker/package.json +1 -1
  145. package/dist/js/components/DescriptionList/package.json +1 -1
  146. package/dist/js/components/Divider/package.json +1 -1
  147. package/dist/js/components/DragDrop/package.json +1 -1
  148. package/dist/js/components/Drawer/package.json +1 -1
  149. package/dist/js/components/Dropdown/Dropdown.d.ts +11 -3
  150. package/dist/js/components/Dropdown/Dropdown.d.ts.map +1 -1
  151. package/dist/js/components/Dropdown/Dropdown.js +11 -5
  152. package/dist/js/components/Dropdown/Dropdown.js.map +1 -1
  153. package/dist/js/components/Dropdown/package.json +1 -1
  154. package/dist/js/components/DualListSelector/package.json +1 -1
  155. package/dist/js/components/EmptyState/package.json +1 -1
  156. package/dist/js/components/ExpandableSection/package.json +1 -1
  157. package/dist/js/components/FileUpload/package.json +1 -1
  158. package/dist/js/components/Form/package.json +1 -1
  159. package/dist/js/components/FormSelect/package.json +1 -1
  160. package/dist/js/components/HelperText/package.json +1 -1
  161. package/dist/js/components/Hint/package.json +1 -1
  162. package/dist/js/components/Icon/package.json +1 -1
  163. package/dist/js/components/InputGroup/package.json +1 -1
  164. package/dist/js/components/JumpLinks/package.json +1 -1
  165. package/dist/js/components/Label/package.json +1 -1
  166. package/dist/js/components/List/package.json +1 -1
  167. package/dist/js/components/LoginPage/package.json +1 -1
  168. package/dist/js/components/Masthead/package.json +1 -1
  169. package/dist/js/components/Menu/package.json +1 -1
  170. package/dist/js/components/MenuToggle/package.json +1 -1
  171. package/dist/js/components/Modal/package.json +1 -1
  172. package/dist/js/components/MultipleFileUpload/package.json +1 -1
  173. package/dist/js/components/Nav/package.json +1 -1
  174. package/dist/js/components/NotificationBadge/package.json +1 -1
  175. package/dist/js/components/NotificationDrawer/package.json +1 -1
  176. package/dist/js/components/NumberInput/package.json +1 -1
  177. package/dist/js/components/OverflowMenu/package.json +1 -1
  178. package/dist/js/components/Page/package.json +1 -1
  179. package/dist/js/components/Pagination/package.json +1 -1
  180. package/dist/js/components/Panel/package.json +1 -1
  181. package/dist/js/components/Popover/package.json +1 -1
  182. package/dist/js/components/Progress/package.json +1 -1
  183. package/dist/js/components/ProgressStepper/package.json +1 -1
  184. package/dist/js/components/Radio/package.json +1 -1
  185. package/dist/js/components/SearchInput/package.json +1 -1
  186. package/dist/js/components/Select/Select.d.ts +31 -5
  187. package/dist/js/components/Select/Select.d.ts.map +1 -1
  188. package/dist/js/components/Select/Select.js +16 -10
  189. package/dist/js/components/Select/Select.js.map +1 -1
  190. package/dist/js/components/Select/SelectGroup.d.ts +3 -0
  191. package/dist/js/components/Select/SelectGroup.d.ts.map +1 -1
  192. package/dist/js/components/Select/SelectGroup.js.map +1 -1
  193. package/dist/js/components/Select/SelectOption.d.ts +14 -1
  194. package/dist/js/components/Select/SelectOption.d.ts.map +1 -1
  195. package/dist/js/components/Select/SelectOption.js +4 -4
  196. package/dist/js/components/Select/SelectOption.js.map +1 -1
  197. package/dist/js/components/Select/package.json +1 -1
  198. package/dist/js/components/Sidebar/package.json +1 -1
  199. package/dist/js/components/SimpleList/package.json +1 -1
  200. package/dist/js/components/Skeleton/package.json +1 -1
  201. package/dist/js/components/SkipToContent/package.json +1 -1
  202. package/dist/js/components/Slider/package.json +1 -1
  203. package/dist/js/components/Spinner/package.json +1 -1
  204. package/dist/js/components/Switch/package.json +1 -1
  205. package/dist/js/components/Tabs/package.json +1 -1
  206. package/dist/js/components/Text/package.json +1 -1
  207. package/dist/js/components/TextArea/package.json +1 -1
  208. package/dist/js/components/TextInput/package.json +1 -1
  209. package/dist/js/components/TextInputGroup/package.json +1 -1
  210. package/dist/js/components/Tile/package.json +1 -1
  211. package/dist/js/components/TimePicker/package.json +1 -1
  212. package/dist/js/components/Timestamp/package.json +1 -1
  213. package/dist/js/components/Title/package.json +1 -1
  214. package/dist/js/components/ToggleGroup/package.json +1 -1
  215. package/dist/js/components/Toolbar/package.json +1 -1
  216. package/dist/js/components/Tooltip/package.json +1 -1
  217. package/dist/js/components/TreeView/package.json +1 -1
  218. package/dist/js/components/Truncate/package.json +1 -1
  219. package/dist/js/components/Wizard/hooks/package.json +1 -1
  220. package/dist/js/components/Wizard/package.json +1 -1
  221. package/dist/js/components/package.json +1 -1
  222. package/dist/js/deprecated/components/ApplicationLauncher/package.json +1 -1
  223. package/dist/js/deprecated/components/ContextSelector/package.json +1 -1
  224. package/dist/js/deprecated/components/Dropdown/package.json +1 -1
  225. package/dist/js/deprecated/components/OptionsMenu/package.json +1 -1
  226. package/dist/js/deprecated/components/PageHeader/package.json +1 -1
  227. package/dist/js/deprecated/components/Select/package.json +1 -1
  228. package/dist/js/deprecated/components/Wizard/package.json +1 -1
  229. package/dist/js/deprecated/components/package.json +1 -1
  230. package/dist/js/deprecated/package.json +1 -1
  231. package/dist/js/helpers/Popper/Popper.d.ts +1 -1
  232. package/dist/js/helpers/Popper/Popper.d.ts.map +1 -1
  233. package/dist/js/helpers/Popper/thirdparty/popper-core/package.json +1 -1
  234. package/dist/js/helpers/package.json +1 -1
  235. package/dist/js/layouts/Bullseye/package.json +1 -1
  236. package/dist/js/layouts/Flex/package.json +1 -1
  237. package/dist/js/layouts/Gallery/package.json +1 -1
  238. package/dist/js/layouts/Grid/package.json +1 -1
  239. package/dist/js/layouts/Level/package.json +1 -1
  240. package/dist/js/layouts/Split/package.json +1 -1
  241. package/dist/js/layouts/Stack/package.json +1 -1
  242. package/dist/js/layouts/package.json +1 -1
  243. package/dist/js/next/components/package.json +1 -1
  244. package/dist/js/next/package.json +1 -1
  245. package/dist/js/package.json +1 -1
  246. package/dist/umd/react-core.min.js +2 -2
  247. package/package.json +2 -2
  248. package/src/components/DatePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap +27 -29
  249. package/src/components/Dropdown/Dropdown.tsx +25 -11
  250. package/src/components/Dropdown/examples/Dropdown.md +10 -1
  251. package/src/components/Dropdown/examples/DropdownBasic.tsx +2 -6
  252. package/src/components/Dropdown/examples/DropdownWithDescriptions.tsx +2 -6
  253. package/src/components/Dropdown/examples/DropdownWithGroups.tsx +2 -6
  254. package/src/components/Dropdown/examples/DropdownWithKebabToggle.tsx +2 -6
  255. package/src/components/Select/Select.tsx +62 -24
  256. package/src/components/Select/SelectGroup.tsx +3 -0
  257. package/src/components/Select/SelectOption.tsx +22 -2
  258. package/src/components/Select/examples/Select.md +49 -2
  259. package/src/components/Select/examples/SelectBasic.tsx +27 -17
  260. package/src/components/Select/examples/SelectCheckbox.tsx +1 -3
  261. package/src/components/Select/examples/SelectFooter.tsx +57 -0
  262. package/src/components/Select/examples/SelectGrouped.tsx +14 -6
  263. package/src/components/Select/examples/SelectMultiTypeahead.tsx +7 -12
  264. package/src/components/Select/examples/SelectMultiTypeaheadCheckbox.tsx +204 -0
  265. package/src/components/Select/examples/SelectMultiTypeaheadCreatable.tsx +222 -0
  266. package/src/components/Select/examples/SelectOptionVariations.tsx +70 -0
  267. package/src/components/Select/examples/SelectTypeahead.tsx +13 -13
  268. package/src/components/Select/examples/SelectTypeaheadCreatable.tsx +220 -0
  269. package/src/components/Select/examples/SelectViewMore.tsx +146 -0
  270. package/src/demos/ComposableMenu/ComposableMenu.md +14 -0
  271. package/src/helpers/Popper/Popper.tsx +1 -1
@@ -26,28 +26,25 @@ export const SelectBasic: React.FunctionComponent = () => {
26
26
  const [isOpen, setIsOpen] = React.useState(false);
27
27
  const [selected, setSelected] = React.useState<string>('');
28
28
  const [inputValue, setInputValue] = React.useState<string>('');
29
+ const [filterValue, setFilterValue] = React.useState<string>('');
29
30
  const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
30
31
  const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
31
32
  const [activeItem, setActiveItem] = React.useState<string | null>(null);
32
-
33
- const menuRef = React.useRef<HTMLDivElement>(null);
34
33
  const textInputRef = React.useRef<HTMLInputElement>();
35
34
 
36
35
  React.useEffect(() => {
37
36
  let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
38
37
 
39
38
  // Filter menu items based on the text input value when one exists
40
- if (inputValue) {
41
- newSelectOptions = initialSelectOptions.filter(menuItem =>
42
- String(menuItem.children)
43
- .toLowerCase()
44
- .includes(inputValue.toLowerCase())
39
+ if (filterValue) {
40
+ newSelectOptions = initialSelectOptions.filter((menuItem) =>
41
+ String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase())
45
42
  );
46
43
 
47
44
  // When no options are found after filtering, display 'No results found'
48
45
  if (!newSelectOptions.length) {
49
46
  newSelectOptions = [
50
- { isDisabled: false, children: `No results found for "${inputValue}"`, itemId: 'no results' }
47
+ { isDisabled: false, children: `No results found for "${filterValue}"`, itemId: 'no results' }
51
48
  ];
52
49
  }
53
50
 
@@ -60,7 +57,7 @@ export const SelectBasic: React.FunctionComponent = () => {
60
57
  setSelectOptions(newSelectOptions);
61
58
  setActiveItem(null);
62
59
  setFocusedItemIndex(null);
63
- }, [inputValue]);
60
+ }, [filterValue]);
64
61
 
65
62
  const onToggleClick = () => {
66
63
  setIsOpen(!isOpen);
@@ -72,6 +69,7 @@ export const SelectBasic: React.FunctionComponent = () => {
72
69
 
73
70
  if (itemId && itemId !== 'no results') {
74
71
  setInputValue(itemId as string);
72
+ setFilterValue('');
75
73
  setSelected(itemId as string);
76
74
  }
77
75
  setIsOpen(false);
@@ -81,6 +79,7 @@ export const SelectBasic: React.FunctionComponent = () => {
81
79
 
82
80
  const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
83
81
  setInputValue(value);
82
+ setFilterValue(value);
84
83
  };
85
84
 
86
85
  const handleMenuArrowKeys = (key: string) => {
@@ -106,13 +105,13 @@ export const SelectBasic: React.FunctionComponent = () => {
106
105
  }
107
106
 
108
107
  setFocusedItemIndex(indexToFocus);
109
- const focusedItem = selectOptions.filter(option => !option.isDisabled)[indexToFocus];
108
+ const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus];
110
109
  setActiveItem(`select-typeahead-${focusedItem.itemId.replace(' ', '-')}`);
111
110
  }
112
111
  };
113
112
 
114
113
  const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
115
- const enabledMenuItems = selectOptions.filter(option => !option.isDisabled);
114
+ const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled);
116
115
  const [firstMenuItem] = enabledMenuItems;
117
116
  const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
118
117
 
@@ -121,10 +120,11 @@ export const SelectBasic: React.FunctionComponent = () => {
121
120
  case 'Enter':
122
121
  if (isOpen && focusedItem.itemId !== 'no results') {
123
122
  setInputValue(String(focusedItem.children));
123
+ setFilterValue('');
124
124
  setSelected(String(focusedItem.children));
125
125
  }
126
126
 
127
- setIsOpen(prevIsOpen => !prevIsOpen);
127
+ setIsOpen((prevIsOpen) => !prevIsOpen);
128
128
  setFocusedItemIndex(null);
129
129
  setActiveItem(null);
130
130
 
@@ -167,6 +167,7 @@ export const SelectBasic: React.FunctionComponent = () => {
167
167
  onClick={() => {
168
168
  setSelected('');
169
169
  setInputValue('');
170
+ setFilterValue('');
170
171
  textInputRef?.current?.focus();
171
172
  }}
172
173
  aria-label="Clear input value"
@@ -182,7 +183,6 @@ export const SelectBasic: React.FunctionComponent = () => {
182
183
  return (
183
184
  <Select
184
185
  id="typeahead-select"
185
- ref={menuRef}
186
186
  isOpen={isOpen}
187
187
  selected={selected}
188
188
  onSelect={onSelect}
@@ -0,0 +1,220 @@
1
+ import React from 'react';
2
+ import {
3
+ Select,
4
+ SelectOption,
5
+ SelectList,
6
+ SelectOptionProps,
7
+ MenuToggle,
8
+ MenuToggleElement,
9
+ TextInputGroup,
10
+ TextInputGroupMain,
11
+ TextInputGroupUtilities,
12
+ Button
13
+ } from '@patternfly/react-core';
14
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
15
+
16
+ let initialSelectOptions: SelectOptionProps[] = [
17
+ { itemId: 'Alabama', children: 'Alabama' },
18
+ { itemId: 'Florida', children: 'Florida' },
19
+ { itemId: 'New Jersey', children: 'New Jersey' },
20
+ { itemId: 'New Mexico', children: 'New Mexico' },
21
+ { itemId: 'New York', children: 'New York' },
22
+ { itemId: 'North Carolina', children: 'North Carolina' }
23
+ ];
24
+
25
+ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
26
+ const [isOpen, setIsOpen] = React.useState(false);
27
+ const [selected, setSelected] = React.useState<string>('');
28
+ const [inputValue, setInputValue] = React.useState<string>('');
29
+ const [filterValue, setFilterValue] = React.useState<string>('');
30
+ const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
31
+ const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
32
+ const [activeItem, setActiveItem] = React.useState<string | null>(null);
33
+ const [onCreation, setOnCreation] = React.useState<boolean>(false); // Boolean to refresh filter state after new option is created
34
+ const textInputRef = React.useRef<HTMLInputElement>();
35
+
36
+ React.useEffect(() => {
37
+ let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
38
+
39
+ // Filter menu items based on the text input value when one exists
40
+ if (filterValue) {
41
+ newSelectOptions = initialSelectOptions.filter((menuItem) =>
42
+ String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase())
43
+ );
44
+
45
+ // When no options are found after filtering, display creation option
46
+ if (!newSelectOptions.length) {
47
+ newSelectOptions = [{ isDisabled: false, children: `Create new option "${filterValue}"`, itemId: 'create' }];
48
+ }
49
+
50
+ // Open the menu when the input value changes and the new value is not empty
51
+ if (!isOpen) {
52
+ setIsOpen(true);
53
+ }
54
+ }
55
+
56
+ setSelectOptions(newSelectOptions);
57
+ setActiveItem(null);
58
+ setFocusedItemIndex(null);
59
+ }, [filterValue, onCreation]);
60
+
61
+ const onToggleClick = () => {
62
+ setIsOpen(!isOpen);
63
+ };
64
+
65
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
66
+ // eslint-disable-next-line no-console
67
+
68
+ if (itemId) {
69
+ if (itemId === 'create') {
70
+ if (!initialSelectOptions.some((item) => item.itemId === filterValue)) {
71
+ initialSelectOptions = [...initialSelectOptions, { itemId: filterValue, children: filterValue }];
72
+ }
73
+ setSelected(filterValue);
74
+ setOnCreation(!onCreation);
75
+ setFilterValue('');
76
+ } else {
77
+ // eslint-disable-next-line no-console
78
+ console.log('selected', itemId);
79
+ setInputValue(itemId as string);
80
+ setFilterValue('');
81
+ setSelected(itemId as string);
82
+ }
83
+ }
84
+
85
+ setIsOpen(false);
86
+ setFocusedItemIndex(null);
87
+ setActiveItem(null);
88
+ };
89
+
90
+ const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
91
+ setInputValue(value);
92
+ setFilterValue(value);
93
+ };
94
+
95
+ const handleMenuArrowKeys = (key: string) => {
96
+ let indexToFocus;
97
+
98
+ if (isOpen) {
99
+ if (key === 'ArrowUp') {
100
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
101
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
102
+ indexToFocus = selectOptions.length - 1;
103
+ } else {
104
+ indexToFocus = focusedItemIndex - 1;
105
+ }
106
+ }
107
+
108
+ if (key === 'ArrowDown') {
109
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
110
+ if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
111
+ indexToFocus = 0;
112
+ } else {
113
+ indexToFocus = focusedItemIndex + 1;
114
+ }
115
+ }
116
+
117
+ setFocusedItemIndex(indexToFocus);
118
+ const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus];
119
+ setActiveItem(`select-create-typeahead-${focusedItem.itemId.replace(' ', '-')}`);
120
+ }
121
+ };
122
+
123
+ const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
124
+ const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled);
125
+ const [firstMenuItem] = enabledMenuItems;
126
+ const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
127
+
128
+ switch (event.key) {
129
+ // Select the first available option
130
+ case 'Enter':
131
+ if (isOpen) {
132
+ onSelect(undefined, focusedItem.itemId as string);
133
+ setIsOpen((prevIsOpen) => !prevIsOpen);
134
+ setFocusedItemIndex(null);
135
+ setActiveItem(null);
136
+ }
137
+
138
+ setIsOpen((prevIsOpen) => !prevIsOpen);
139
+ setFocusedItemIndex(null);
140
+ setActiveItem(null);
141
+
142
+ break;
143
+ case 'Tab':
144
+ case 'Escape':
145
+ setIsOpen(false);
146
+ setActiveItem(null);
147
+ break;
148
+ case 'ArrowUp':
149
+ case 'ArrowDown':
150
+ event.preventDefault();
151
+ handleMenuArrowKeys(event.key);
152
+ break;
153
+ }
154
+ };
155
+
156
+ const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
157
+ <MenuToggle ref={toggleRef} variant="typeahead" onClick={onToggleClick} isExpanded={isOpen} isFullWidth>
158
+ <TextInputGroup isPlain>
159
+ <TextInputGroupMain
160
+ value={inputValue}
161
+ onClick={onToggleClick}
162
+ onChange={onTextInputChange}
163
+ onKeyDown={onInputKeyDown}
164
+ id="create-typeahead-select-input"
165
+ autoComplete="off"
166
+ innerRef={textInputRef}
167
+ placeholder="Select a state"
168
+ {...(activeItem && { 'aria-activedescendant': activeItem })}
169
+ role="combobox"
170
+ isExpanded={isOpen}
171
+ aria-controls="select-create-typeahead-listbox"
172
+ />
173
+
174
+ <TextInputGroupUtilities>
175
+ {!!inputValue && (
176
+ <Button
177
+ variant="plain"
178
+ onClick={() => {
179
+ setSelected('');
180
+ setInputValue('');
181
+ setFilterValue('');
182
+ textInputRef?.current?.focus();
183
+ }}
184
+ aria-label="Clear input value"
185
+ >
186
+ <TimesIcon aria-hidden />
187
+ </Button>
188
+ )}
189
+ </TextInputGroupUtilities>
190
+ </TextInputGroup>
191
+ </MenuToggle>
192
+ );
193
+
194
+ return (
195
+ <Select
196
+ id="create-typeahead-select"
197
+ isOpen={isOpen}
198
+ selected={selected}
199
+ onSelect={onSelect}
200
+ onOpenChange={() => {
201
+ setIsOpen(false);
202
+ }}
203
+ toggle={toggle}
204
+ >
205
+ <SelectList id="select-create-typeahead-listbox">
206
+ {selectOptions.map((option, index) => (
207
+ <SelectOption
208
+ key={option.itemId || option.children}
209
+ isFocused={focusedItemIndex === index}
210
+ className={option.className}
211
+ onClick={() => setSelected(option.itemId)}
212
+ id={`select-typeahead-${option.itemId.replace(' ', '-')}`}
213
+ {...option}
214
+ ref={null}
215
+ />
216
+ ))}
217
+ </SelectList>
218
+ </Select>
219
+ );
220
+ };
@@ -0,0 +1,146 @@
1
+ import React from 'react';
2
+ import { Select, SelectOption, SelectList, MenuToggle, Spinner } from '@patternfly/react-core';
3
+
4
+ export const SelectViewMore: React.FunctionComponent = () => {
5
+ const [isOpen, setIsOpen] = React.useState(false);
6
+ const [selected, setSelected] = React.useState<string>('Select a value');
7
+ const [activeItem, setActiveItem] = React.useState<number | string>(0);
8
+ const [isLoading, setIsLoading] = React.useState(false);
9
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
10
+ const [selectOptions, setSelectOptions] = React.useState([
11
+ <SelectOption key={0} itemId="Option 1">
12
+ Option 1
13
+ </SelectOption>,
14
+ <SelectOption key={1} itemId="Option 2">
15
+ Option 2
16
+ </SelectOption>,
17
+ <SelectOption key={2} itemId="Option 3">
18
+ Option 3
19
+ </SelectOption>,
20
+ <SelectOption key={3} itemId="Option 4">
21
+ Option 4
22
+ </SelectOption>,
23
+ <SelectOption key={4} itemId="Option 5">
24
+ Option 5
25
+ </SelectOption>,
26
+ <SelectOption key={5} itemId="Option 6">
27
+ Option 6
28
+ </SelectOption>,
29
+ <SelectOption key={6} itemId="Option 7">
30
+ Option 7
31
+ </SelectOption>,
32
+ <SelectOption key={7} itemId="Option 8">
33
+ Option 8
34
+ </SelectOption>,
35
+ <SelectOption key={8} itemId="Option 9">
36
+ Option 9
37
+ </SelectOption>,
38
+ <SelectOption key={9} itemId="Final Option 10">
39
+ Final Option 10
40
+ </SelectOption>
41
+ ]);
42
+ const [numOptions, setNumOptions] = React.useState(3);
43
+ const [visibleOptions, setVisibleOptions] = React.useState(selectOptions.slice(0, numOptions));
44
+ const activeItemRef = React.useRef<HTMLElement>(null);
45
+ const viewMoreRef = React.useRef<HTMLLIElement>(null);
46
+ const toggleRef = React.useRef<HTMLButtonElement>(null);
47
+
48
+ React.useEffect(() => {
49
+ activeItemRef.current?.focus();
50
+ }, [visibleOptions]);
51
+
52
+ const simulateNetworkCall = (networkCallback: () => void) => {
53
+ setTimeout(networkCallback, 2000);
54
+ };
55
+
56
+ const getNextValidItem = (startingIndex: number, maxLength: number) => {
57
+ let validItem;
58
+ for (let i = startingIndex; i < maxLength; i++) {
59
+ if (selectOptions[i].props.isDisabled) {
60
+ continue;
61
+ } else {
62
+ validItem = selectOptions[i];
63
+ break;
64
+ }
65
+ }
66
+ return validItem;
67
+ };
68
+
69
+ const loadMoreOptions = () => {
70
+ const newLength = numOptions + 3 <= selectOptions.length ? numOptions + 3 : selectOptions.length;
71
+ const prevPosition = numOptions;
72
+ const nextValidItem = getNextValidItem(prevPosition, newLength);
73
+
74
+ setNumOptions(newLength);
75
+ setIsLoading(false);
76
+ setActiveItem(nextValidItem.props.itemId);
77
+ setVisibleOptions(selectOptions.slice(0, newLength));
78
+ };
79
+
80
+ const onViewMoreClick = () => {
81
+ setIsLoading(true);
82
+ simulateNetworkCall(() => {
83
+ loadMoreOptions();
84
+ });
85
+ };
86
+
87
+ const onToggleClick = () => {
88
+ setIsOpen(!isOpen);
89
+ };
90
+
91
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
92
+ // eslint-disable-next-line no-console
93
+ console.log('selected', itemId);
94
+
95
+ if (itemId !== 'loader') {
96
+ setSelected(itemId as string);
97
+ setIsOpen(false);
98
+ toggleRef?.current?.focus(); // Only focus the toggle when a non-loader option is selected
99
+ }
100
+ };
101
+
102
+ const toggle = (
103
+ <MenuToggle
104
+ ref={toggleRef}
105
+ onClick={onToggleClick}
106
+ isExpanded={isOpen}
107
+ style={
108
+ {
109
+ width: '200px'
110
+ } as React.CSSProperties
111
+ }
112
+ >
113
+ {selected}
114
+ </MenuToggle>
115
+ );
116
+
117
+ return (
118
+ <Select
119
+ id="single-view-more-select"
120
+ isOpen={isOpen}
121
+ selected={selected}
122
+ onSelect={onSelect}
123
+ onOpenChange={(isOpen) => setIsOpen(isOpen)}
124
+ toggle={{ toggleNode: toggle, toggleRef }}
125
+ >
126
+ <SelectList>
127
+ {visibleOptions.map((option) => {
128
+ const props = option.props;
129
+
130
+ return <SelectOption key={option.key} ref={props.itemId === activeItem ? activeItemRef : null} {...props} />;
131
+ })}
132
+ {numOptions !== selectOptions.length && (
133
+ <SelectOption
134
+ {...(!isLoading && { isLoadButton: true })}
135
+ {...(isLoading && { isLoading: true })}
136
+ onClick={onViewMoreClick}
137
+ itemId="loader"
138
+ ref={viewMoreRef}
139
+ >
140
+ {isLoading ? <Spinner size="lg" /> : 'View more'}
141
+ </SelectOption>
142
+ )}
143
+ </SelectList>
144
+ </Select>
145
+ );
146
+ };
@@ -32,36 +32,43 @@ Custom menus can be constructed using a composable approach by combining the [Me
32
32
  ### Composable simple dropdown
33
33
 
34
34
  ```ts file="./examples/ComposableSimpleDropdown.tsx"
35
+
35
36
  ```
36
37
 
37
38
  ### Composable actions menu
38
39
 
39
40
  ```ts file="./examples/ComposableActionsMenu.tsx"
41
+
40
42
  ```
41
43
 
42
44
  ### Composable simple select
43
45
 
44
46
  ```ts file="./examples/ComposableSimpleSelect.tsx"
47
+
45
48
  ```
46
49
 
47
50
  ### Composable simple checkbox select
48
51
 
49
52
  ```ts file="./examples/ComposableSimpleCheckboxSelect.tsx"
53
+
50
54
  ```
51
55
 
52
56
  ### Composable typeahead select
53
57
 
54
58
  ```ts file="./examples/ComposableTypeaheadSelect.tsx"
59
+
55
60
  ```
56
61
 
57
62
  ### Composable multiple typeahead select
58
63
 
59
64
  ```ts file="./examples/ComposableMultipleTypeaheadSelect.tsx"
65
+
60
66
  ```
61
67
 
62
68
  ### Composable drilldown menu
63
69
 
64
70
  ```ts isBeta file="./examples/ComposableDrilldownMenu.tsx"
71
+
65
72
  ```
66
73
 
67
74
  ### Composable tree view menu
@@ -69,6 +76,7 @@ Custom menus can be constructed using a composable approach by combining the [Me
69
76
  When rendering a menu-like element that does not contain MenuItem components, [Panel](/components/panel) allows more flexible control and customization.
70
77
 
71
78
  ```ts file="./examples/ComposableTreeViewMenu.tsx"
79
+
72
80
  ```
73
81
 
74
82
  ### Composable flyout
@@ -76,29 +84,35 @@ When rendering a menu-like element that does not contain MenuItem components, [P
76
84
  The flyout will automatically position to the left or top if it would otherwise go outside the window. The menu must be placed in a container outside the main content like Popper, [Popover](/components/popover) or [Tooltip](/components/tooltip) since it may go over the side nav.
77
85
 
78
86
  ```ts isBeta file="./examples/ComposableFlyout.tsx"
87
+
79
88
  ```
80
89
 
81
90
  ### Composable application launcher
82
91
 
83
92
  ```ts file="./examples/ComposableApplicationLauncher.tsx"
93
+
84
94
  ```
85
95
 
86
96
  ### Composable context selector
87
97
 
88
98
  ```ts file="./examples/ComposableContextSelector.tsx"
99
+
89
100
  ```
90
101
 
91
102
  ### Composable options menu variants
92
103
 
93
104
  ```ts file="./examples/ComposableOptionsMenuVariants.tsx"
105
+
94
106
  ```
95
107
 
96
108
  ### Composable dropdown variants
97
109
 
98
110
  ```ts file="./examples/ComposableDropdwnVariants.tsx"
111
+
99
112
  ```
100
113
 
101
114
  ### Composable date select
102
115
 
103
116
  ```ts file="./examples/ComposableDateSelect.tsx"
117
+
104
118
  ```
@@ -68,7 +68,7 @@ export interface PopperProps {
68
68
  position?: 'right' | 'left' | 'center';
69
69
  /** Instead of direction and position can set the placement of the popper */
70
70
  placement?: Placement;
71
- /** Custsom width of the popper. If the value is "trigger", it will set the width to the trigger element's width */
71
+ /** Custom width of the popper. If the value is "trigger", it will set the width to the trigger element's width */
72
72
  width?: string | 'trigger';
73
73
  /** Minimum width of the popper. If the value is "trigger", it will set the min width to the trigger element's width */
74
74
  minWidth?: string | 'trigger';