@rhc-shared-components/form-multi-select-component 1.0.3 → 1.0.5

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.
@@ -1,20 +1,18 @@
1
- import React from 'react';
2
- import { SelectOptionProps } from '@patternfly/react-core';
3
- declare type IMultiSelectInputOptionProps = SelectOptionProps;
4
- export interface FormMultiSelectInputProps {
5
- name: string;
6
- label: string;
7
- isRequired?: boolean;
8
- placeholder?: string;
9
- selectMenuOptions: IMultiSelectInputOptionProps[];
10
- ariaLabel?: string;
11
- helperText?: string;
12
- maxHeight?: string;
13
- isDisabled?: boolean;
14
- classNames?: string;
15
- menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
16
- extraProps?: any;
17
- isScrollable?: boolean;
18
- }
19
- declare const FormMultiSelectInput: React.FunctionComponent<FormMultiSelectInputProps>;
20
- export { FormMultiSelectInput, IMultiSelectInputOptionProps };
1
+ import React from 'react';
2
+ import { SelectOptionProps } from '@patternfly/react-core';
3
+ export interface FormMultiSelectInputProps {
4
+ name: string;
5
+ label: string;
6
+ isRequired?: boolean;
7
+ placeholder?: string;
8
+ selectMenuOptions: SelectOptionProps[];
9
+ ariaLabel?: string;
10
+ helperText?: string;
11
+ maxHeight?: string;
12
+ isDisabled?: boolean;
13
+ classNames?: string;
14
+ extraProps?: any;
15
+ isScrollable?: boolean;
16
+ }
17
+ declare const FormMultiSelectInput: React.FunctionComponent<FormMultiSelectInputProps>;
18
+ export { FormMultiSelectInput };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { FormMultiSelectInput, IMultiSelectInputOptionProps } from './FormMultiSelectInput';
2
- export { FormMultiSelectInput, IMultiSelectInputOptionProps };
1
+ import { FormMultiSelectInput } from './FormMultiSelectInput';
2
+ export { FormMultiSelectInput };
package/dist/index.js CHANGED
@@ -26,6 +26,24 @@ function _interopNamespace(e) {
26
26
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
27
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
28
28
 
29
+ function _extends() {
30
+ _extends = Object.assign || function (target) {
31
+ for (var i = 1; i < arguments.length; i++) {
32
+ var source = arguments[i];
33
+
34
+ for (var key in source) {
35
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
36
+ target[key] = source[key];
37
+ }
38
+ }
39
+ }
40
+
41
+ return target;
42
+ };
43
+
44
+ return _extends.apply(this, arguments);
45
+ }
46
+
29
47
  function _objectWithoutPropertiesLoose(source, excluded) {
30
48
  if (source == null) return {};
31
49
  var target = {};
@@ -111,7 +129,7 @@ const TimesIcon = createIcon(TimesIconConfig);
111
129
 
112
130
  var TimesIcon$1 = TimesIcon;
113
131
 
114
- var _excluded = ["label", "isRequired", "children", "selectMenuOptions", "ariaLabel", "placeholder", "helperText", "maxHeight", "isDisabled", "classNames", "menuAppendTo", "extraProps", "isScrollable"];
132
+ var _excluded = ["label", "isRequired", "children", "selectMenuOptions", "ariaLabel", "placeholder", "helperText", "maxHeight", "isDisabled", "classNames", "extraProps", "isScrollable"];
115
133
 
116
134
  var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
117
135
  var label = _ref.label,
@@ -120,8 +138,8 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
120
138
  placeholder = _ref.placeholder,
121
139
  helperText = _ref.helperText,
122
140
  maxHeight = _ref.maxHeight,
123
- isDisabled = _ref.isDisabled,
124
- menuAppendTo = _ref.menuAppendTo,
141
+ _ref$isDisabled = _ref.isDisabled,
142
+ isDisabled = _ref$isDisabled === void 0 ? false : _ref$isDisabled,
125
143
  rest = _objectWithoutPropertiesLoose(_ref, _excluded);
126
144
 
127
145
  var _useField = formik.useField(rest),
@@ -155,10 +173,11 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
155
173
  setFocusedItemIndex = _React$useState5[1];
156
174
 
157
175
  var _React$useState6 = React__default["default"].useState(null),
158
- activeItem = _React$useState6[0],
159
- setActiveItem = _React$useState6[1];
176
+ activeItemId = _React$useState6[0],
177
+ setActiveItemId = _React$useState6[1];
160
178
 
161
179
  var textInputRef = React__default["default"].useRef();
180
+ var NO_RESULTS = 'no results';
162
181
  React__default["default"].useEffect(function () {
163
182
  var newSelectOptions = selectMenuOptions; // Filter menu items based on the text input value when one exists
164
183
 
@@ -169,9 +188,9 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
169
188
 
170
189
  if (!newSelectOptions.length) {
171
190
  newSelectOptions = [{
172
- isDisabled: false,
191
+ isAriaDisabled: true,
173
192
  children: "No results found for \"" + inputValue + "\"",
174
- value: 'no results'
193
+ value: NO_RESULTS
175
194
  }];
176
195
  } // Open the menu when the input value changes and the new value is not empty
177
196
 
@@ -182,8 +201,7 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
182
201
  }
183
202
 
184
203
  setSelectOptions(newSelectOptions);
185
- setActiveItem(null);
186
- }, [inputValue, selectMenuOptions]);
204
+ }, [inputValue]);
187
205
  React__default["default"].useEffect(function () {
188
206
  if ((value == null ? void 0 : value.length) > 0) {
189
207
  setFieldValue(rest.name, value, true);
@@ -191,62 +209,122 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
191
209
  }
192
210
  }, []);
193
211
 
212
+ var createItemId = function createItemId(value) {
213
+ return "select-multi-typeahead-" + value.replace(' ', '-');
214
+ };
215
+
216
+ var setActiveAndFocusedItem = function setActiveAndFocusedItem(itemIndex) {
217
+ setFocusedItemIndex(itemIndex);
218
+ var focusedItem = selectOptions[itemIndex];
219
+ setActiveItemId(createItemId(focusedItem.value));
220
+ };
221
+
222
+ var resetActiveAndFocusedItem = function resetActiveAndFocusedItem() {
223
+ setFocusedItemIndex(null);
224
+ setActiveItemId(null);
225
+ };
226
+
227
+ var closeMenu = function closeMenu() {
228
+ setIsOpen(false);
229
+ resetActiveAndFocusedItem();
230
+ };
231
+
232
+ var onInputClick = function onInputClick() {
233
+ if (!isOpen) {
234
+ setIsOpen(true);
235
+ } else if (!inputValue) {
236
+ closeMenu();
237
+ }
238
+ };
239
+
240
+ var _onSelect = function onSelect(value) {
241
+ var _textInputRef$current;
242
+
243
+ if (value && value !== NO_RESULTS) {
244
+ var selectedValues = selected.includes(value) ? selected.filter(function (selection) {
245
+ return selection !== value;
246
+ }) : [].concat(selected, [value]);
247
+ setSelected(selectedValues);
248
+ setFieldTouched(rest.name, true, false);
249
+ setFieldValue(rest.name, selectedValues, true);
250
+ } // eslint-disable-next-line no-unused-expressions
251
+
252
+
253
+ (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
254
+ };
255
+
256
+ var onTextInputChange = function onTextInputChange(_event, value) {
257
+ setInputValue(value);
258
+ resetActiveAndFocusedItem();
259
+ };
260
+
194
261
  var handleMenuArrowKeys = function handleMenuArrowKeys(key) {
195
262
  var indexToFocus = 0;
196
263
 
197
- if (isOpen) {
198
- if (key === 'ArrowUp') {
199
- // When no index is set or at the first index, focus to the last, otherwise decrement focus index
200
- if (focusedItemIndex === null || focusedItemIndex === 0) {
264
+ if (!isOpen) {
265
+ setIsOpen(true);
266
+ }
267
+
268
+ if (selectOptions.every(function (option) {
269
+ return option.isDisabled;
270
+ })) {
271
+ return;
272
+ }
273
+
274
+ if (key === 'ArrowUp') {
275
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
276
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
277
+ indexToFocus = selectOptions.length - 1;
278
+ } else {
279
+ indexToFocus = focusedItemIndex - 1;
280
+ } // Skip disabled options
281
+
282
+
283
+ while (selectOptions[indexToFocus].isDisabled) {
284
+ indexToFocus--;
285
+
286
+ if (indexToFocus === -1) {
201
287
  indexToFocus = selectOptions.length - 1;
202
- } else {
203
- indexToFocus = focusedItemIndex - 1;
204
288
  }
205
289
  }
290
+ }
291
+
292
+ if (key === 'ArrowDown') {
293
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
294
+ if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
295
+ indexToFocus = 0;
296
+ } else {
297
+ indexToFocus = focusedItemIndex + 1;
298
+ } // Skip disabled options
299
+
206
300
 
207
- if (key === 'ArrowDown') {
208
- // When no index is set or at the last index, focus to the first, otherwise increment focus index
209
- if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
301
+ while (selectOptions[indexToFocus].isDisabled) {
302
+ indexToFocus++;
303
+
304
+ if (indexToFocus === selectOptions.length) {
210
305
  indexToFocus = 0;
211
- } else {
212
- indexToFocus = focusedItemIndex + 1;
213
306
  }
214
307
  }
215
-
216
- setFocusedItemIndex(indexToFocus);
217
- var focusedItem = selectOptions.filter(function (option) {
218
- return !option.isDisabled;
219
- })[indexToFocus];
220
- setActiveItem("select-multi-typeahead-" + focusedItem.value.replace(' ', '-'));
221
308
  }
309
+
310
+ setActiveAndFocusedItem(indexToFocus);
222
311
  };
223
312
 
224
313
  var onInputKeyDown = function onInputKeyDown(event) {
225
- var enabledMenuItems = selectOptions.filter(function (menuItem) {
226
- return !menuItem.isDisabled;
227
- });
228
- var firstMenuItem = enabledMenuItems[0];
229
- var focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
314
+ var focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null;
230
315
 
231
316
  switch (event.key) {
232
- // Select the first available option
233
317
  case 'Enter':
234
- if (!isOpen) {
235
- setIsOpen(function (prevIsOpen) {
236
- return !prevIsOpen;
237
- });
238
- } else if (isOpen && focusedItem.value !== 'no results') {
239
- event.preventDefault();
318
+ event.preventDefault();
240
319
 
320
+ if (isOpen && focusedItem && focusedItem.value !== NO_RESULTS && !focusedItem.isAriaDisabled) {
241
321
  _onSelect(focusedItem.value);
242
322
  }
243
323
 
244
- break;
324
+ if (!isOpen) {
325
+ setIsOpen(true);
326
+ }
245
327
 
246
- case 'Tab':
247
- case 'Escape':
248
- setIsOpen(false);
249
- setActiveItem(null);
250
328
  break;
251
329
 
252
330
  case 'ArrowUp':
@@ -258,36 +336,35 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
258
336
  };
259
337
 
260
338
  var onToggleClick = function onToggleClick() {
261
- setIsOpen(!isOpen);
262
- };
339
+ var _textInputRef$current2;
263
340
 
264
- var onTextInputChange = function onTextInputChange(_event, value) {
265
- setInputValue(value);
266
- setFocusedItemIndex(null);
267
- };
341
+ setIsOpen(!isOpen); // eslint-disable-next-line no-unused-expressions
268
342
 
269
- var _onSelect = function onSelect(value) {
270
- var _textInputRef$current;
343
+ textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
344
+ };
271
345
 
272
- // eslint-disable-next-line no-console
273
- console.log('selected', value);
346
+ var onClearButtonClick = function onClearButtonClick() {
347
+ var _textInputRef$current3;
274
348
 
275
- if (value && value !== 'no results') {
276
- var selectedValues = selected.includes(value) ? selected.filter(function (selection) {
277
- return selection !== value;
278
- }) : [].concat(selected, [value]);
279
- setSelected(selectedValues);
280
- setFieldTouched(rest.name, true, false);
281
- setFieldValue(rest.name, selectedValues, true);
282
- } // eslint-disable-next-line no-unused-expressions
349
+ setSelected([]);
350
+ setInputValue('');
351
+ resetActiveAndFocusedItem();
352
+ textInputRef == null ? void 0 : (_textInputRef$current3 = textInputRef.current) == null ? void 0 : _textInputRef$current3.focus();
353
+ setFieldValue(rest.name, [], true);
354
+ };
283
355
 
356
+ var getChildren = function getChildren(value) {
357
+ var _selectMenuOptions$fi;
284
358
 
285
- textInputRef == null ? void 0 : (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
359
+ return (_selectMenuOptions$fi = selectMenuOptions.find(function (option) {
360
+ return option.value === value;
361
+ })) == null ? void 0 : _selectMenuOptions$fi.children;
286
362
  };
287
363
 
288
364
  var toggle = function toggle(toggleRef) {
289
365
  return React__default["default"].createElement(reactCore.MenuToggle, {
290
366
  variant: 'typeahead',
367
+ "aria-label": 'menu toggle',
291
368
  onClick: onToggleClick,
292
369
  innerRef: toggleRef,
293
370
  isExpanded: isOpen,
@@ -295,17 +372,17 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
295
372
  isFullWidth: true
296
373
  }, React__default["default"].createElement(reactCore.TextInputGroup, {
297
374
  isPlain: true
298
- }, React__default["default"].createElement(reactCore.TextInputGroupMain, Object.assign({
375
+ }, React__default["default"].createElement(reactCore.TextInputGroupMain, _extends({
299
376
  value: inputValue,
300
- onClick: onToggleClick,
377
+ onClick: onInputClick,
301
378
  onChange: onTextInputChange,
302
379
  onKeyDown: onInputKeyDown,
303
380
  id: 'multi-typeahead-select-input',
304
381
  autoComplete: 'off',
305
382
  innerRef: textInputRef,
306
383
  placeholder: placeholder
307
- }, activeItem && {
308
- 'aria-activedescendant': activeItem
384
+ }, activeItemId && {
385
+ 'aria-activedescendant': activeItemId
309
386
  }, {
310
387
  role: 'combobox',
311
388
  isExpanded: isOpen,
@@ -320,57 +397,52 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
320
397
 
321
398
  _onSelect(selection);
322
399
  }
323
- }, selection);
324
- }))), React__default["default"].createElement(reactCore.TextInputGroupUtilities, null, selected.length > 0 && React__default["default"].createElement(reactCore.Button, {
400
+ }, getChildren(selection));
401
+ }))), React__default["default"].createElement(reactCore.TextInputGroupUtilities, _extends({}, selected.length === 0 ? {
402
+ style: {
403
+ display: 'none'
404
+ }
405
+ } : {}), React__default["default"].createElement(reactCore.Button, {
325
406
  variant: 'plain',
326
- onClick: function onClick() {
327
- var _textInputRef$current2;
328
-
329
- setInputValue('');
330
- setSelected([]); // eslint-disable-next-line no-unused-expressions
331
-
332
- textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
333
- setFieldValue(rest.name, [], true);
334
- },
407
+ onClick: onClearButtonClick,
335
408
  "aria-label": 'Clear input value'
336
409
  }, React__default["default"].createElement(TimesIcon$1, {
337
410
  "aria-hidden": true
338
411
  })))));
339
412
  };
340
413
 
341
- return React__default["default"].createElement(React__default["default"].Fragment, null, React__default["default"].createElement(formGroupContainer.FormGroupContainer, {
414
+ return React__default["default"].createElement(formGroupContainer.FormGroupContainer, {
342
415
  validated: meta.touched && meta.error ? reactCore.ValidatedOptions.error : reactCore.ValidatedOptions["default"],
343
416
  helperTextInvalid: meta.error,
344
417
  isRequired: isRequired,
345
418
  fieldId: rest.name,
346
419
  label: label,
347
420
  helperText: helperText
348
- }, React__default["default"].createElement(reactCore.Select, Object.assign({
421
+ }, React__default["default"].createElement(reactCore.Select, {
349
422
  id: 'multi-typeahead-select',
350
423
  isOpen: isOpen,
351
424
  selected: selected,
352
425
  onSelect: function onSelect(_ev, selection) {
353
426
  return _onSelect(selection);
354
427
  },
355
- onOpenChange: function onOpenChange() {
356
- return setIsOpen(false);
357
- },
358
428
  toggle: toggle,
359
429
  isScrollable: true,
360
- maxMenuHeight: maxHeight
361
- }, menuAppendTo && {
362
- menuAppendTo: menuAppendTo
363
- }), React__default["default"].createElement(reactCore.SelectList, {
430
+ maxMenuHeight: maxHeight,
431
+ onOpenChange: function onOpenChange(isOpen) {
432
+ !isOpen && closeMenu();
433
+ }
434
+ }, React__default["default"].createElement(reactCore.SelectList, {
364
435
  isAriaMultiselectable: true,
365
- id: 'select-multi-typeahead-listbox'
436
+ id: "select-multi-typeahead-listbox"
366
437
  }, selectOptions == null ? void 0 : selectOptions.map(function (option, index) {
367
- return React__default["default"].createElement(reactCore.SelectOption, Object.assign({
438
+ return React__default["default"].createElement(reactCore.SelectOption, _extends({
368
439
  key: option.value || option.children,
369
440
  isFocused: focusedItemIndex === index,
370
- id: "select-multi-typeahead-" + option.value.replace(' ', '-'),
441
+ className: option.className,
442
+ id: createItemId(option.value),
371
443
  isDisabled: option.isDisabled || isSubmitting
372
444
  }, option));
373
- })))));
445
+ }))));
374
446
  };
375
447
 
376
448
  exports.FormMultiSelectInput = FormMultiSelectInput;
@@ -4,6 +4,24 @@ import { ValidatedOptions, Select, SelectList, SelectOption, MenuToggle, TextInp
4
4
  import { FormGroupContainer } from '@rhc-shared-components/form-group-container';
5
5
  import { useField, useFormikContext } from 'formik';
6
6
 
7
+ function _extends() {
8
+ _extends = Object.assign || function (target) {
9
+ for (var i = 1; i < arguments.length; i++) {
10
+ var source = arguments[i];
11
+
12
+ for (var key in source) {
13
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
14
+ target[key] = source[key];
15
+ }
16
+ }
17
+ }
18
+
19
+ return target;
20
+ };
21
+
22
+ return _extends.apply(this, arguments);
23
+ }
24
+
7
25
  function _objectWithoutPropertiesLoose(source, excluded) {
8
26
  if (source == null) return {};
9
27
  var target = {};
@@ -89,19 +107,18 @@ const TimesIcon = createIcon(TimesIconConfig);
89
107
 
90
108
  var TimesIcon$1 = TimesIcon;
91
109
 
92
- const _excluded = ["label", "isRequired", "children", "selectMenuOptions", "ariaLabel", "placeholder", "helperText", "maxHeight", "isDisabled", "classNames", "menuAppendTo", "extraProps", "isScrollable"];
110
+ const _excluded = ["label", "isRequired", "children", "selectMenuOptions", "ariaLabel", "placeholder", "helperText", "maxHeight", "isDisabled", "classNames", "extraProps", "isScrollable"];
93
111
 
94
112
  const FormMultiSelectInput = _ref => {
95
113
  let {
96
114
  label,
97
115
  isRequired,
98
116
  selectMenuOptions,
99
- ariaLabel = 'Select Input',
117
+ ariaLabel = 'Select a value',
100
118
  placeholder,
101
119
  helperText,
102
120
  maxHeight,
103
- isDisabled,
104
- menuAppendTo
121
+ isDisabled = false
105
122
  } = _ref,
106
123
  rest = _objectWithoutPropertiesLoose(_ref, _excluded);
107
124
 
@@ -119,8 +136,9 @@ const FormMultiSelectInput = _ref => {
119
136
  const [selected, setSelected] = React__default.useState([]);
120
137
  const [selectOptions, setSelectOptions] = React__default.useState(selectMenuOptions);
121
138
  const [focusedItemIndex, setFocusedItemIndex] = React__default.useState(null);
122
- const [activeItem, setActiveItem] = React__default.useState(null);
139
+ const [activeItemId, setActiveItemId] = React__default.useState(null);
123
140
  const textInputRef = React__default.useRef();
141
+ const NO_RESULTS = 'no results';
124
142
  React__default.useEffect(() => {
125
143
  let newSelectOptions = selectMenuOptions; // Filter menu items based on the text input value when one exists
126
144
 
@@ -129,9 +147,9 @@ const FormMultiSelectInput = _ref => {
129
147
 
130
148
  if (!newSelectOptions.length) {
131
149
  newSelectOptions = [{
132
- isDisabled: false,
150
+ isAriaDisabled: true,
133
151
  children: `No results found for "${inputValue}"`,
134
- value: 'no results'
152
+ value: NO_RESULTS
135
153
  }];
136
154
  } // Open the menu when the input value changes and the new value is not empty
137
155
 
@@ -142,8 +160,7 @@ const FormMultiSelectInput = _ref => {
142
160
  }
143
161
 
144
162
  setSelectOptions(newSelectOptions);
145
- setActiveItem(null);
146
- }, [inputValue, selectMenuOptions]);
163
+ }, [inputValue]);
147
164
  React__default.useEffect(() => {
148
165
  if ((value == null ? void 0 : value.length) > 0) {
149
166
  setFieldValue(rest.name, value, true);
@@ -151,55 +168,116 @@ const FormMultiSelectInput = _ref => {
151
168
  }
152
169
  }, []);
153
170
 
171
+ const createItemId = value => `select-multi-typeahead-${value.replace(' ', '-')}`;
172
+
173
+ const setActiveAndFocusedItem = itemIndex => {
174
+ setFocusedItemIndex(itemIndex);
175
+ const focusedItem = selectOptions[itemIndex];
176
+ setActiveItemId(createItemId(focusedItem.value));
177
+ };
178
+
179
+ const resetActiveAndFocusedItem = () => {
180
+ setFocusedItemIndex(null);
181
+ setActiveItemId(null);
182
+ };
183
+
184
+ const closeMenu = () => {
185
+ setIsOpen(false);
186
+ resetActiveAndFocusedItem();
187
+ };
188
+
189
+ const onInputClick = () => {
190
+ if (!isOpen) {
191
+ setIsOpen(true);
192
+ } else if (!inputValue) {
193
+ closeMenu();
194
+ }
195
+ };
196
+
197
+ const onSelect = value => {
198
+ var _textInputRef$current;
199
+
200
+ if (value && value !== NO_RESULTS) {
201
+ const selectedValues = selected.includes(value) ? selected.filter(selection => selection !== value) : [...selected, value];
202
+ setSelected(selectedValues);
203
+ setFieldTouched(rest.name, true, false);
204
+ setFieldValue(rest.name, selectedValues, true);
205
+ } // eslint-disable-next-line no-unused-expressions
206
+
207
+
208
+ (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
209
+ };
210
+
211
+ const onTextInputChange = (_event, value) => {
212
+ setInputValue(value);
213
+ resetActiveAndFocusedItem();
214
+ };
215
+
154
216
  const handleMenuArrowKeys = key => {
155
217
  let indexToFocus = 0;
156
218
 
157
- if (isOpen) {
158
- if (key === 'ArrowUp') {
159
- // When no index is set or at the first index, focus to the last, otherwise decrement focus index
160
- if (focusedItemIndex === null || focusedItemIndex === 0) {
219
+ if (!isOpen) {
220
+ setIsOpen(true);
221
+ }
222
+
223
+ if (selectOptions.every(option => option.isDisabled)) {
224
+ return;
225
+ }
226
+
227
+ if (key === 'ArrowUp') {
228
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
229
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
230
+ indexToFocus = selectOptions.length - 1;
231
+ } else {
232
+ indexToFocus = focusedItemIndex - 1;
233
+ } // Skip disabled options
234
+
235
+
236
+ while (selectOptions[indexToFocus].isDisabled) {
237
+ indexToFocus--;
238
+
239
+ if (indexToFocus === -1) {
161
240
  indexToFocus = selectOptions.length - 1;
162
- } else {
163
- indexToFocus = focusedItemIndex - 1;
164
241
  }
165
242
  }
243
+ }
244
+
245
+ if (key === 'ArrowDown') {
246
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
247
+ if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
248
+ indexToFocus = 0;
249
+ } else {
250
+ indexToFocus = focusedItemIndex + 1;
251
+ } // Skip disabled options
252
+
253
+
254
+ while (selectOptions[indexToFocus].isDisabled) {
255
+ indexToFocus++;
166
256
 
167
- if (key === 'ArrowDown') {
168
- // When no index is set or at the last index, focus to the first, otherwise increment focus index
169
- if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
257
+ if (indexToFocus === selectOptions.length) {
170
258
  indexToFocus = 0;
171
- } else {
172
- indexToFocus = focusedItemIndex + 1;
173
259
  }
174
260
  }
175
-
176
- setFocusedItemIndex(indexToFocus);
177
- const focusedItem = selectOptions.filter(option => !option.isDisabled)[indexToFocus];
178
- setActiveItem(`select-multi-typeahead-${focusedItem.value.replace(' ', '-')}`);
179
261
  }
262
+
263
+ setActiveAndFocusedItem(indexToFocus);
180
264
  };
181
265
 
182
266
  const onInputKeyDown = event => {
183
- const enabledMenuItems = selectOptions.filter(menuItem => !menuItem.isDisabled);
184
- const [firstMenuItem] = enabledMenuItems;
185
- const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
267
+ const focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null;
186
268
 
187
269
  switch (event.key) {
188
- // Select the first available option
189
270
  case 'Enter':
190
- if (!isOpen) {
191
- setIsOpen(prevIsOpen => !prevIsOpen);
192
- } else if (isOpen && focusedItem.value !== 'no results') {
193
- event.preventDefault();
271
+ event.preventDefault();
272
+
273
+ if (isOpen && focusedItem && focusedItem.value !== NO_RESULTS && !focusedItem.isAriaDisabled) {
194
274
  onSelect(focusedItem.value);
195
275
  }
196
276
 
197
- break;
277
+ if (!isOpen) {
278
+ setIsOpen(true);
279
+ }
198
280
 
199
- case 'Tab':
200
- case 'Escape':
201
- setIsOpen(false);
202
- setActiveItem(null);
203
281
  break;
204
282
 
205
283
  case 'ArrowUp':
@@ -211,33 +289,32 @@ const FormMultiSelectInput = _ref => {
211
289
  };
212
290
 
213
291
  const onToggleClick = () => {
214
- setIsOpen(!isOpen);
215
- };
292
+ var _textInputRef$current2;
216
293
 
217
- const onTextInputChange = (_event, value) => {
218
- setInputValue(value);
219
- setFocusedItemIndex(null);
220
- };
294
+ setIsOpen(!isOpen); // eslint-disable-next-line no-unused-expressions
221
295
 
222
- const onSelect = value => {
223
- var _textInputRef$current;
296
+ textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
297
+ };
224
298
 
225
- // eslint-disable-next-line no-console
226
- console.log('selected', value);
299
+ const onClearButtonClick = () => {
300
+ var _textInputRef$current3;
227
301
 
228
- if (value && value !== 'no results') {
229
- const selectedValues = selected.includes(value) ? selected.filter(selection => selection !== value) : [...selected, value];
230
- setSelected(selectedValues);
231
- setFieldTouched(rest.name, true, false);
232
- setFieldValue(rest.name, selectedValues, true);
233
- } // eslint-disable-next-line no-unused-expressions
302
+ setSelected([]);
303
+ setInputValue('');
304
+ resetActiveAndFocusedItem();
305
+ textInputRef == null ? void 0 : (_textInputRef$current3 = textInputRef.current) == null ? void 0 : _textInputRef$current3.focus();
306
+ setFieldValue(rest.name, [], true);
307
+ };
234
308
 
309
+ const getChildren = value => {
310
+ var _selectMenuOptions$fi;
235
311
 
236
- textInputRef == null ? void 0 : (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
312
+ return (_selectMenuOptions$fi = selectMenuOptions.find(option => option.value === value)) == null ? void 0 : _selectMenuOptions$fi.children;
237
313
  };
238
314
 
239
315
  const toggle = toggleRef => React__default.createElement(MenuToggle, {
240
316
  variant: 'typeahead',
317
+ "aria-label": 'menu toggle',
241
318
  onClick: onToggleClick,
242
319
  innerRef: toggleRef,
243
320
  isExpanded: isOpen,
@@ -245,17 +322,17 @@ const FormMultiSelectInput = _ref => {
245
322
  isFullWidth: true
246
323
  }, React__default.createElement(TextInputGroup, {
247
324
  isPlain: true
248
- }, React__default.createElement(TextInputGroupMain, Object.assign({
325
+ }, React__default.createElement(TextInputGroupMain, _extends({
249
326
  value: inputValue,
250
- onClick: onToggleClick,
327
+ onClick: onInputClick,
251
328
  onChange: onTextInputChange,
252
329
  onKeyDown: onInputKeyDown,
253
330
  id: 'multi-typeahead-select-input',
254
331
  autoComplete: 'off',
255
332
  innerRef: textInputRef,
256
333
  placeholder: placeholder
257
- }, activeItem && {
258
- 'aria-activedescendant': activeItem
334
+ }, activeItemId && {
335
+ 'aria-activedescendant': activeItemId
259
336
  }, {
260
337
  role: 'combobox',
261
338
  isExpanded: isOpen,
@@ -268,49 +345,46 @@ const FormMultiSelectInput = _ref => {
268
345
  ev.stopPropagation();
269
346
  onSelect(selection);
270
347
  }
271
- }, selection)))), React__default.createElement(TextInputGroupUtilities, null, selected.length > 0 && React__default.createElement(Button, {
348
+ }, getChildren(selection))))), React__default.createElement(TextInputGroupUtilities, _extends({}, selected.length === 0 ? {
349
+ style: {
350
+ display: 'none'
351
+ }
352
+ } : {}), React__default.createElement(Button, {
272
353
  variant: 'plain',
273
- onClick: () => {
274
- var _textInputRef$current2;
275
-
276
- setInputValue('');
277
- setSelected([]); // eslint-disable-next-line no-unused-expressions
278
-
279
- textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
280
- setFieldValue(rest.name, [], true);
281
- },
354
+ onClick: onClearButtonClick,
282
355
  "aria-label": 'Clear input value'
283
356
  }, React__default.createElement(TimesIcon$1, {
284
357
  "aria-hidden": true
285
358
  })))));
286
359
 
287
- return React__default.createElement(React__default.Fragment, null, React__default.createElement(FormGroupContainer, {
360
+ return React__default.createElement(FormGroupContainer, {
288
361
  validated: meta.touched && meta.error ? ValidatedOptions.error : ValidatedOptions.default,
289
362
  helperTextInvalid: meta.error,
290
363
  isRequired: isRequired,
291
364
  fieldId: rest.name,
292
365
  label: label,
293
366
  helperText: helperText
294
- }, React__default.createElement(Select, Object.assign({
367
+ }, React__default.createElement(Select, {
295
368
  id: 'multi-typeahead-select',
296
369
  isOpen: isOpen,
297
370
  selected: selected,
298
371
  onSelect: (_ev, selection) => onSelect(selection),
299
- onOpenChange: () => setIsOpen(false),
300
372
  toggle: toggle,
301
373
  isScrollable: true,
302
- maxMenuHeight: maxHeight
303
- }, menuAppendTo && {
304
- menuAppendTo
305
- }), React__default.createElement(SelectList, {
374
+ maxMenuHeight: maxHeight,
375
+ onOpenChange: isOpen => {
376
+ !isOpen && closeMenu();
377
+ }
378
+ }, React__default.createElement(SelectList, {
306
379
  isAriaMultiselectable: true,
307
- id: 'select-multi-typeahead-listbox'
308
- }, selectOptions == null ? void 0 : selectOptions.map((option, index) => React__default.createElement(SelectOption, Object.assign({
380
+ id: "select-multi-typeahead-listbox"
381
+ }, selectOptions == null ? void 0 : selectOptions.map((option, index) => React__default.createElement(SelectOption, _extends({
309
382
  key: option.value || option.children,
310
383
  isFocused: focusedItemIndex === index,
311
- id: `select-multi-typeahead-${option.value.replace(' ', '-')}`,
384
+ className: option.className,
385
+ id: createItemId(option.value),
312
386
  isDisabled: option.isDisabled || isSubmitting
313
- }, option)))))));
387
+ }, option))))));
314
388
  };
315
389
 
316
390
  export { FormMultiSelectInput };
@@ -1 +1 @@
1
- export {};
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhc-shared-components/form-multi-select-component",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "project description",
5
5
  "author": "shkale",
6
6
  "license": "MIT",
@@ -59,7 +59,7 @@
59
59
  "react-scripts": "^3.4.1",
60
60
  "rimraf": "^3.0.2",
61
61
  "sass": "^1.57.1",
62
- "typescript": "^3.7.5"
62
+ "typescript": "~5.7.2"
63
63
  },
64
64
  "dependencies": {
65
65
  "@patternfly/react-core": "5.3.0",
package/CHANGELOG.md DELETED
@@ -1,65 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- ## [0.0.8](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/compare/@rhc-shared-components/form-multi-select-component@0.0.7...@rhc-shared-components/form-multi-select-component@0.0.8) (2022-12-06)
7
-
8
- **Note:** Version bump only for package @rhc-shared-components/form-multi-select-component
9
-
10
-
11
-
12
-
13
-
14
- ## [0.0.7](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/compare/@rhc-shared-components/form-multi-select-component@0.0.6...@rhc-shared-components/form-multi-select-component@0.0.7) (2022-12-06)
15
-
16
- **Note:** Version bump only for package @rhc-shared-components/form-multi-select-component
17
-
18
-
19
-
20
-
21
-
22
- ## [0.0.6](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/compare/@rhc-shared-components/form-multi-select-component@0.0.4...@rhc-shared-components/form-multi-select-component@0.0.6) (2022-12-06)
23
-
24
-
25
- ### Bug Fixes
26
-
27
- * added additional options ([a4e49e7](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/commit/a4e49e7a049b40583200f96cc2f7f9456e4ea99c))
28
- * fixed bugs removed max filter, updated eg ([3fedea1](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/commit/3fedea120e3d93de365f2c9b19202ffe7f601612))
29
-
30
-
31
-
32
-
33
-
34
- ## [0.0.4](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/compare/@rhc-shared-components/form-multi-select-component@0.0.2...@rhc-shared-components/form-multi-select-component@0.0.4) (2022-10-27)
35
-
36
-
37
- ### Bug Fixes
38
-
39
- * added setTouched prop in multiselect component. ([3b0b6ff](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/commit/3b0b6ff0445995129bc2b2908861768a513339d8))
40
- * added touched handler ([82e397e](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/commit/82e397e3c8ae90944d3110dd5d7b3d5ff98b0589))
41
-
42
-
43
-
44
- ## 0.0.3 (2022-10-27)
45
-
46
-
47
- ### Bug Fixes
48
-
49
- * added meta.touched prop to the form multi select component. ([590886f](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/commit/590886fd8eb49a2153f6d5b512c6bd61cbae205c))
50
-
51
-
52
-
53
-
54
-
55
- ## [0.0.2](https://gitlab.cee.redhat.com/customer-platform/rhc-shared-components/compare/@rhc-shared-components/form-multi-select-component@0.0.1...@rhc-shared-components/form-multi-select-component@0.0.2) (2022-01-05)
56
-
57
- **Note:** Version bump only for package @rhc-shared-components/form-multi-select-component
58
-
59
-
60
-
61
-
62
-
63
- ## 0.0.1 (2021-12-06)
64
-
65
- **Note:** Version bump only for package @rhc-shared-components/form-multi-select-component