@patternfly/react-core 5.3.2 → 5.3.4

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 (147) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/AboutModal/package.json +1 -1
  5. package/dist/dynamic/components/Accordion/package.json +1 -1
  6. package/dist/dynamic/components/ActionList/package.json +1 -1
  7. package/dist/dynamic/components/Alert/package.json +1 -1
  8. package/dist/dynamic/components/Avatar/package.json +1 -1
  9. package/dist/dynamic/components/BackToTop/package.json +1 -1
  10. package/dist/dynamic/components/Backdrop/package.json +1 -1
  11. package/dist/dynamic/components/BackgroundImage/package.json +1 -1
  12. package/dist/dynamic/components/Badge/package.json +1 -1
  13. package/dist/dynamic/components/Banner/package.json +1 -1
  14. package/dist/dynamic/components/Brand/package.json +1 -1
  15. package/dist/dynamic/components/Breadcrumb/package.json +1 -1
  16. package/dist/dynamic/components/Button/package.json +1 -1
  17. package/dist/dynamic/components/CalendarMonth/package.json +1 -1
  18. package/dist/dynamic/components/Card/package.json +1 -1
  19. package/dist/dynamic/components/Checkbox/package.json +1 -1
  20. package/dist/dynamic/components/Chip/package.json +1 -1
  21. package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
  22. package/dist/dynamic/components/CodeBlock/package.json +1 -1
  23. package/dist/dynamic/components/DataList/package.json +1 -1
  24. package/dist/dynamic/components/DatePicker/package.json +1 -1
  25. package/dist/dynamic/components/DescriptionList/package.json +1 -1
  26. package/dist/dynamic/components/Divider/package.json +1 -1
  27. package/dist/dynamic/components/DragDrop/package.json +1 -1
  28. package/dist/dynamic/components/Drawer/package.json +1 -1
  29. package/dist/dynamic/components/Dropdown/package.json +1 -1
  30. package/dist/dynamic/components/DualListSelector/package.json +1 -1
  31. package/dist/dynamic/components/EmptyState/package.json +1 -1
  32. package/dist/dynamic/components/ExpandableSection/package.json +1 -1
  33. package/dist/dynamic/components/FileUpload/package.json +1 -1
  34. package/dist/dynamic/components/Form/package.json +1 -1
  35. package/dist/dynamic/components/FormSelect/package.json +1 -1
  36. package/dist/dynamic/components/HelperText/package.json +1 -1
  37. package/dist/dynamic/components/Hint/package.json +1 -1
  38. package/dist/dynamic/components/Icon/package.json +1 -1
  39. package/dist/dynamic/components/InputGroup/package.json +1 -1
  40. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  41. package/dist/dynamic/components/Label/package.json +1 -1
  42. package/dist/dynamic/components/List/package.json +1 -1
  43. package/dist/dynamic/components/LoginPage/package.json +1 -1
  44. package/dist/dynamic/components/Masthead/package.json +1 -1
  45. package/dist/dynamic/components/Menu/package.json +1 -1
  46. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  47. package/dist/dynamic/components/Modal/package.json +1 -1
  48. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  49. package/dist/dynamic/components/Nav/package.json +1 -1
  50. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  51. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  52. package/dist/dynamic/components/NumberInput/package.json +1 -1
  53. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  54. package/dist/dynamic/components/Page/package.json +1 -1
  55. package/dist/dynamic/components/Pagination/package.json +1 -1
  56. package/dist/dynamic/components/Panel/package.json +1 -1
  57. package/dist/dynamic/components/Popover/package.json +1 -1
  58. package/dist/dynamic/components/Progress/package.json +1 -1
  59. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  60. package/dist/dynamic/components/Radio/package.json +1 -1
  61. package/dist/dynamic/components/SearchInput/package.json +1 -1
  62. package/dist/dynamic/components/Select/package.json +1 -1
  63. package/dist/dynamic/components/Sidebar/package.json +1 -1
  64. package/dist/dynamic/components/SimpleList/package.json +1 -1
  65. package/dist/dynamic/components/Skeleton/package.json +1 -1
  66. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  67. package/dist/dynamic/components/Slider/package.json +1 -1
  68. package/dist/dynamic/components/Spinner/package.json +1 -1
  69. package/dist/dynamic/components/Switch/package.json +1 -1
  70. package/dist/dynamic/components/Tabs/package.json +1 -1
  71. package/dist/dynamic/components/Text/package.json +1 -1
  72. package/dist/dynamic/components/TextArea/package.json +1 -1
  73. package/dist/dynamic/components/TextInput/package.json +1 -1
  74. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  75. package/dist/dynamic/components/Tile/package.json +1 -1
  76. package/dist/dynamic/components/TimePicker/package.json +1 -1
  77. package/dist/dynamic/components/Timestamp/package.json +1 -1
  78. package/dist/dynamic/components/Title/package.json +1 -1
  79. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  80. package/dist/dynamic/components/Toolbar/package.json +1 -1
  81. package/dist/dynamic/components/Tooltip/package.json +1 -1
  82. package/dist/dynamic/components/TreeView/package.json +1 -1
  83. package/dist/dynamic/components/Truncate/package.json +1 -1
  84. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  85. package/dist/dynamic/components/Wizard/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/ApplicationLauncher/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/ContextSelector/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Dropdown/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/OptionsMenu/package.json +1 -1
  90. package/dist/dynamic/deprecated/components/PageHeader/package.json +1 -1
  91. package/dist/dynamic/deprecated/components/Select/package.json +1 -1
  92. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  93. package/dist/dynamic/deprecated/components/package.json +1 -1
  94. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  95. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  96. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  97. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  98. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  99. package/dist/dynamic/helpers/constants/package.json +1 -1
  100. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  101. package/dist/dynamic/helpers/favorites/package.json +1 -1
  102. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  103. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  104. package/dist/dynamic/helpers/package.json +1 -1
  105. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  106. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  107. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  108. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  109. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  110. package/dist/dynamic/helpers/util/package.json +1 -1
  111. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  112. package/dist/dynamic/layouts/Flex/package.json +1 -1
  113. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  114. package/dist/dynamic/layouts/Grid/package.json +1 -1
  115. package/dist/dynamic/layouts/Level/package.json +1 -1
  116. package/dist/dynamic/layouts/Split/package.json +1 -1
  117. package/dist/dynamic/layouts/Stack/package.json +1 -1
  118. package/dist/dynamic/next/components/DualListSelector/package.json +1 -1
  119. package/dist/dynamic/next/components/Modal/package.json +1 -1
  120. package/dist/dynamic/next/components/package.json +1 -1
  121. package/dist/dynamic/styles/package.json +1 -1
  122. package/dist/esm/components/MenuToggle/MenuToggle.d.ts.map +1 -1
  123. package/dist/esm/components/MenuToggle/MenuToggle.js +1 -1
  124. package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
  125. package/dist/esm/components/Select/Select.d.ts +2 -0
  126. package/dist/esm/components/Select/Select.d.ts.map +1 -1
  127. package/dist/esm/components/Select/Select.js +2 -2
  128. package/dist/esm/components/Select/Select.js.map +1 -1
  129. package/dist/js/components/MenuToggle/MenuToggle.d.ts.map +1 -1
  130. package/dist/js/components/MenuToggle/MenuToggle.js +1 -1
  131. package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
  132. package/dist/js/components/Select/Select.d.ts +2 -0
  133. package/dist/js/components/Select/Select.d.ts.map +1 -1
  134. package/dist/js/components/Select/Select.js +2 -2
  135. package/dist/js/components/Select/Select.js.map +1 -1
  136. package/dist/umd/react-core.min.js +1 -1
  137. package/helpers/package.json +1 -1
  138. package/layouts/package.json +1 -1
  139. package/next/package.json +1 -1
  140. package/package.json +6 -6
  141. package/src/components/MenuToggle/MenuToggle.tsx +1 -0
  142. package/src/components/Select/Select.tsx +4 -1
  143. package/src/components/Select/examples/SelectMultiTypeahead.tsx +111 -66
  144. package/src/components/Select/examples/SelectMultiTypeaheadCheckbox.tsx +103 -60
  145. package/src/components/Select/examples/SelectMultiTypeaheadCreatable.tsx +125 -79
  146. package/src/components/Select/examples/SelectTypeahead.tsx +113 -69
  147. package/src/components/Select/examples/SelectTypeaheadCreatable.tsx +115 -75
@@ -29,10 +29,11 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
29
29
  const [filterValue, setFilterValue] = React.useState<string>('');
30
30
  const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
31
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
32
+ const [activeItemId, setActiveItemId] = React.useState<string | null>(null);
34
33
  const textInputRef = React.useRef<HTMLInputElement>();
35
34
 
35
+ const CREATE_NEW = 'create';
36
+
36
37
  React.useEffect(() => {
37
38
  let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
38
39
 
@@ -42,9 +43,9 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
42
43
  String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase())
43
44
  );
44
45
 
45
- // When no options are found after filtering, display creation option
46
- if (!newSelectOptions.length) {
47
- newSelectOptions = [{ isDisabled: false, children: `Create new option "${filterValue}"`, value: 'create' }];
46
+ // If no option matches the filter exactly, display creation option
47
+ if (!initialSelectOptions.some((option) => option.value === filterValue)) {
48
+ newSelectOptions = [...newSelectOptions, { children: `Create new option "${filterValue}"`, value: CREATE_NEW }];
48
49
  }
49
50
 
50
51
  // Open the menu when the input value changes and the new value is not empty
@@ -54,96 +55,133 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
54
55
  }
55
56
 
56
57
  setSelectOptions(newSelectOptions);
57
- setActiveItem(null);
58
+ }, [filterValue]);
59
+
60
+ const createItemId = (value: any) => `select-typeahead-${value.replace(' ', '-')}`;
61
+
62
+ const setActiveAndFocusedItem = (itemIndex: number) => {
63
+ setFocusedItemIndex(itemIndex);
64
+ const focusedItem = selectOptions[itemIndex];
65
+ setActiveItemId(createItemId(focusedItem.value));
66
+ };
67
+
68
+ const resetActiveAndFocusedItem = () => {
58
69
  setFocusedItemIndex(null);
59
- }, [filterValue, onCreation]);
70
+ setActiveItemId(null);
71
+ };
60
72
 
61
- const onToggleClick = () => {
62
- setIsOpen(!isOpen);
73
+ const closeMenu = () => {
74
+ setIsOpen(false);
75
+ resetActiveAndFocusedItem();
63
76
  };
64
77
 
65
- const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
78
+ const onInputClick = () => {
79
+ if (!isOpen) {
80
+ setIsOpen(true);
81
+ } else if (!inputValue) {
82
+ closeMenu();
83
+ }
84
+ };
85
+
86
+ const selectOption = (value: string | number, content: string | number) => {
66
87
  // eslint-disable-next-line no-console
88
+ console.log('selected', content);
89
+
90
+ setInputValue(String(content));
91
+ setFilterValue('');
92
+ setSelected(String(value));
93
+
94
+ closeMenu();
95
+ };
67
96
 
97
+ const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
68
98
  if (value) {
69
- if (value === 'create') {
70
- if (!initialSelectOptions.some((item) => item.value === filterValue)) {
99
+ if (value === CREATE_NEW) {
100
+ if (!initialSelectOptions.some((item) => item.children === filterValue)) {
71
101
  initialSelectOptions = [...initialSelectOptions, { value: filterValue, children: filterValue }];
72
102
  }
73
103
  setSelected(filterValue);
74
- setOnCreation(!onCreation);
75
104
  setFilterValue('');
105
+ closeMenu();
76
106
  } else {
77
- // eslint-disable-next-line no-console
78
- console.log('selected', value);
79
- setInputValue(value as string);
80
- setFilterValue('');
81
- setSelected(value as string);
107
+ const optionText = selectOptions.find((option) => option.value === value)?.children;
108
+ selectOption(value, optionText as string);
82
109
  }
83
110
  }
84
-
85
- setIsOpen(false);
86
- setFocusedItemIndex(null);
87
- setActiveItem(null);
88
111
  };
89
112
 
90
113
  const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
91
114
  setInputValue(value);
92
115
  setFilterValue(value);
116
+
117
+ resetActiveAndFocusedItem();
118
+
119
+ if (value !== selected) {
120
+ setSelected('');
121
+ }
93
122
  };
94
123
 
95
124
  const handleMenuArrowKeys = (key: string) => {
96
- let indexToFocus;
125
+ let indexToFocus = 0;
126
+
127
+ if (!isOpen) {
128
+ setIsOpen(true);
129
+ }
97
130
 
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) {
131
+ if (selectOptions.every((option) => option.isDisabled)) {
132
+ return;
133
+ }
134
+
135
+ if (key === 'ArrowUp') {
136
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
137
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
138
+ indexToFocus = selectOptions.length - 1;
139
+ } else {
140
+ indexToFocus = focusedItemIndex - 1;
141
+ }
142
+
143
+ // Skip disabled options
144
+ while (selectOptions[indexToFocus].isDisabled) {
145
+ indexToFocus--;
146
+ if (indexToFocus === -1) {
102
147
  indexToFocus = selectOptions.length - 1;
103
- } else {
104
- indexToFocus = focusedItemIndex - 1;
105
148
  }
106
149
  }
150
+ }
151
+
152
+ if (key === 'ArrowDown') {
153
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
154
+ if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
155
+ indexToFocus = 0;
156
+ } else {
157
+ indexToFocus = focusedItemIndex + 1;
158
+ }
107
159
 
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) {
160
+ // Skip disabled options
161
+ while (selectOptions[indexToFocus].isDisabled) {
162
+ indexToFocus++;
163
+ if (indexToFocus === selectOptions.length) {
111
164
  indexToFocus = 0;
112
- } else {
113
- indexToFocus = focusedItemIndex + 1;
114
165
  }
115
166
  }
116
-
117
- setFocusedItemIndex(indexToFocus);
118
- const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus];
119
- setActiveItem(`select-create-typeahead-${focusedItem.value.replace(' ', '-')}`);
120
167
  }
168
+
169
+ setActiveAndFocusedItem(indexToFocus);
121
170
  };
122
171
 
123
172
  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;
173
+ const focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null;
127
174
 
128
175
  switch (event.key) {
129
- // Select the first available option
130
176
  case 'Enter':
131
- if (isOpen) {
177
+ if (isOpen && focusedItem && !focusedItem.isAriaDisabled) {
132
178
  onSelect(undefined, focusedItem.value as string);
133
- setIsOpen((prevIsOpen) => !prevIsOpen);
134
- setFocusedItemIndex(null);
135
- setActiveItem(null);
136
179
  }
137
180
 
138
- setIsOpen((prevIsOpen) => !prevIsOpen);
139
- setFocusedItemIndex(null);
140
- setActiveItem(null);
181
+ if (!isOpen) {
182
+ setIsOpen(true);
183
+ }
141
184
 
142
- break;
143
- case 'Tab':
144
- case 'Escape':
145
- setIsOpen(false);
146
- setActiveItem(null);
147
185
  break;
148
186
  case 'ArrowUp':
149
187
  case 'ArrowDown':
@@ -153,6 +191,19 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
153
191
  }
154
192
  };
155
193
 
194
+ const onToggleClick = () => {
195
+ setIsOpen(!isOpen);
196
+ textInputRef?.current?.focus();
197
+ };
198
+
199
+ const onClearButtonClick = () => {
200
+ setSelected('');
201
+ setInputValue('');
202
+ setFilterValue('');
203
+ resetActiveAndFocusedItem();
204
+ textInputRef?.current?.focus();
205
+ };
206
+
156
207
  const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
157
208
  <MenuToggle
158
209
  ref={toggleRef}
@@ -165,34 +216,23 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
165
216
  <TextInputGroup isPlain>
166
217
  <TextInputGroupMain
167
218
  value={inputValue}
168
- onClick={onToggleClick}
219
+ onClick={onInputClick}
169
220
  onChange={onTextInputChange}
170
221
  onKeyDown={onInputKeyDown}
171
222
  id="create-typeahead-select-input"
172
223
  autoComplete="off"
173
224
  innerRef={textInputRef}
174
225
  placeholder="Select a state"
175
- {...(activeItem && { 'aria-activedescendant': activeItem })}
226
+ {...(activeItemId && { 'aria-activedescendant': activeItemId })}
176
227
  role="combobox"
177
228
  isExpanded={isOpen}
178
229
  aria-controls="select-create-typeahead-listbox"
179
230
  />
180
231
 
181
- <TextInputGroupUtilities>
182
- {!!inputValue && (
183
- <Button
184
- variant="plain"
185
- onClick={() => {
186
- setSelected('');
187
- setInputValue('');
188
- setFilterValue('');
189
- textInputRef?.current?.focus();
190
- }}
191
- aria-label="Clear input value"
192
- >
193
- <TimesIcon aria-hidden />
194
- </Button>
195
- )}
232
+ <TextInputGroupUtilities {...(!inputValue ? { style: { display: 'none' } } : {})}>
233
+ <Button variant="plain" onClick={onClearButtonClick} aria-label="Clear input value">
234
+ <TimesIcon aria-hidden />
235
+ </Button>
196
236
  </TextInputGroupUtilities>
197
237
  </TextInputGroup>
198
238
  </MenuToggle>
@@ -204,10 +244,11 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
204
244
  isOpen={isOpen}
205
245
  selected={selected}
206
246
  onSelect={onSelect}
207
- onOpenChange={() => {
208
- setIsOpen(false);
247
+ onOpenChange={(isOpen) => {
248
+ !isOpen && closeMenu();
209
249
  }}
210
250
  toggle={toggle}
251
+ shouldFocusFirstItemOnOpen={false}
211
252
  >
212
253
  <SelectList id="select-create-typeahead-listbox">
213
254
  {selectOptions.map((option, index) => (
@@ -215,8 +256,7 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
215
256
  key={option.value || option.children}
216
257
  isFocused={focusedItemIndex === index}
217
258
  className={option.className}
218
- onClick={() => setSelected(option.value)}
219
- id={`select-typeahead-${option.value.replace(' ', '-')}`}
259
+ id={createItemId(option.value)}
220
260
  {...option}
221
261
  ref={null}
222
262
  />