@patternfly/react-core 4.222.11 → 4.222.14

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 (64) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/esm/components/CalendarMonth/CalendarMonth.d.ts +3 -1
  3. package/dist/esm/components/CalendarMonth/CalendarMonth.d.ts.map +1 -1
  4. package/dist/esm/components/CalendarMonth/CalendarMonth.js +21 -14
  5. package/dist/esm/components/CalendarMonth/CalendarMonth.js.map +1 -1
  6. package/dist/esm/components/DatePicker/DatePicker.d.ts +14 -4
  7. package/dist/esm/components/DatePicker/DatePicker.d.ts.map +1 -1
  8. package/dist/esm/components/DatePicker/DatePicker.js +20 -8
  9. package/dist/esm/components/DatePicker/DatePicker.js.map +1 -1
  10. package/dist/esm/components/Label/Label.js +3 -3
  11. package/dist/esm/components/Label/Label.js.map +1 -1
  12. package/dist/esm/components/LabelGroup/LabelGroup.d.ts +2 -0
  13. package/dist/esm/components/LabelGroup/LabelGroup.d.ts.map +1 -1
  14. package/dist/esm/components/LabelGroup/LabelGroup.js +4 -3
  15. package/dist/esm/components/LabelGroup/LabelGroup.js.map +1 -1
  16. package/dist/esm/components/Popover/Popover.js +2 -2
  17. package/dist/esm/components/Popover/Popover.js.map +1 -1
  18. package/dist/esm/components/TimePicker/TimePicker.d.ts +6 -1
  19. package/dist/esm/components/TimePicker/TimePicker.d.ts.map +1 -1
  20. package/dist/esm/components/TimePicker/TimePicker.js +32 -19
  21. package/dist/esm/components/TimePicker/TimePicker.js.map +1 -1
  22. package/dist/js/components/CalendarMonth/CalendarMonth.d.ts +3 -1
  23. package/dist/js/components/CalendarMonth/CalendarMonth.d.ts.map +1 -1
  24. package/dist/js/components/CalendarMonth/CalendarMonth.js +21 -14
  25. package/dist/js/components/CalendarMonth/CalendarMonth.js.map +1 -1
  26. package/dist/js/components/DatePicker/DatePicker.d.ts +14 -4
  27. package/dist/js/components/DatePicker/DatePicker.d.ts.map +1 -1
  28. package/dist/js/components/DatePicker/DatePicker.js +20 -8
  29. package/dist/js/components/DatePicker/DatePicker.js.map +1 -1
  30. package/dist/js/components/Label/Label.js +3 -3
  31. package/dist/js/components/Label/Label.js.map +1 -1
  32. package/dist/js/components/LabelGroup/LabelGroup.d.ts +2 -0
  33. package/dist/js/components/LabelGroup/LabelGroup.d.ts.map +1 -1
  34. package/dist/js/components/LabelGroup/LabelGroup.js +4 -3
  35. package/dist/js/components/LabelGroup/LabelGroup.js.map +1 -1
  36. package/dist/js/components/Popover/Popover.js +1 -1
  37. package/dist/js/components/Popover/Popover.js.map +1 -1
  38. package/dist/js/components/TimePicker/TimePicker.d.ts +6 -1
  39. package/dist/js/components/TimePicker/TimePicker.d.ts.map +1 -1
  40. package/dist/js/components/TimePicker/TimePicker.js +32 -19
  41. package/dist/js/components/TimePicker/TimePicker.js.map +1 -1
  42. package/dist/umd/react-core.css +21 -0
  43. package/dist/umd/react-core.js +83 -49
  44. package/dist/umd/react-core.min.css +21 -0
  45. package/dist/umd/react-core.min.js +2 -2
  46. package/package.json +6 -6
  47. package/src/components/CalendarMonth/CalendarMonth.tsx +21 -13
  48. package/src/components/DatePicker/DatePicker.tsx +36 -11
  49. package/src/components/Label/Label.tsx +3 -3
  50. package/src/components/LabelGroup/LabelGroup.tsx +5 -1
  51. package/src/components/LabelGroup/examples/LabelGroup.md +7 -0
  52. package/src/components/LabelGroup/examples/LabelGroupEditableAdd.tsx +74 -0
  53. package/src/components/LabelGroup/examples/LabelGroupEditableAddDropdown.tsx +138 -0
  54. package/src/components/LabelGroup/examples/LabelGroupEditableAddModal.tsx +356 -0
  55. package/src/components/Modal/examples/Modal.md +2 -0
  56. package/src/components/OverflowMenu/examples/OverflowMenu.md +8 -0
  57. package/src/components/Popover/Popover.tsx +2 -2
  58. package/src/components/TimePicker/TimePicker.tsx +36 -19
  59. package/src/components/Toolbar/examples/Toolbar.md +4 -0
  60. package/src/demos/Card/Card.md +2 -0
  61. package/src/demos/{DatePicker.md → DatePicker/DatePicker.md} +8 -0
  62. package/src/demos/DatePicker/examples/DateTimePickerInModal.tsx +51 -0
  63. package/src/demos/LabelGroupDemos.md +18 -0
  64. package/src/demos/PrimaryDetail.md +10 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.222.11",
3
+ "version": "4.222.14",
4
4
  "description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -34,16 +34,16 @@
34
34
  "generate": "node scripts/copyStyles.js"
35
35
  },
36
36
  "dependencies": {
37
- "@patternfly/react-icons": "^4.73.11",
38
- "@patternfly/react-styles": "^4.72.11",
39
- "@patternfly/react-tokens": "^4.74.11",
37
+ "@patternfly/react-icons": "^4.73.14",
38
+ "@patternfly/react-styles": "^4.72.14",
39
+ "@patternfly/react-tokens": "^4.74.14",
40
40
  "focus-trap": "6.9.2",
41
41
  "react-dropzone": "9.0.0",
42
42
  "tippy.js": "5.1.2",
43
43
  "tslib": "^2.0.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@patternfly/patternfly": "4.197.0",
46
+ "@patternfly/patternfly": "4.198.2",
47
47
  "@rollup/plugin-commonjs": "^21.0.0",
48
48
  "@rollup/plugin-node-resolve": "^13.0.0",
49
49
  "@rollup/plugin-replace": "^3.0.0",
@@ -60,5 +60,5 @@
60
60
  "react": "^16.8.0 || ^17.0.0",
61
61
  "react-dom": "^16.8.0 || ^17.0.0"
62
62
  },
63
- "gitHead": "f1a199ba0c0555f0e78faf42e7d25fa5e6510871"
63
+ "gitHead": "0d04c7d4c7970f2be7e485e1f4bd0bda94ee5d46"
64
64
  }
@@ -55,6 +55,8 @@ export interface CalendarProps extends CalendarFormat, Omit<React.HTMLProps<HTML
55
55
  className?: string;
56
56
  /** @hide Internal prop to allow pressing escape in select menu to not close popover */
57
57
  onSelectToggle?: (open: boolean) => void;
58
+ /** Flag to set browser focus on the passed date **/
59
+ isDateFocused?: boolean;
58
60
  }
59
61
 
60
62
  // Must be numeric given current header design
@@ -110,19 +112,24 @@ export const CalendarMonth = ({
110
112
  nextMonthAriaLabel = 'Next month',
111
113
  yearInputAriaLabel = 'Select year',
112
114
  cellAriaLabel,
115
+ isDateFocused = false,
113
116
  ...props
114
117
  }: CalendarProps) => {
115
118
  const longMonths = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(monthNum => new Date(1990, monthNum)).map(monthFormat);
116
119
  const [isSelectOpen, setIsSelectOpen] = React.useState(false);
117
120
  // eslint-disable-next-line prefer-const
118
- let [focusedDate, setFocusedDate] = React.useState(new Date(dateProp));
119
- if (!isValidDate(focusedDate)) {
120
- if (isValidDate(rangeStart)) {
121
- focusedDate = rangeStart;
121
+ const [focusedDate, setFocusedDate] = React.useState(() => {
122
+ const initDate = new Date(dateProp);
123
+ if (isValidDate(initDate)) {
124
+ return initDate;
122
125
  } else {
123
- focusedDate = today;
126
+ if (isValidDate(rangeStart)) {
127
+ return rangeStart;
128
+ } else {
129
+ return today;
130
+ }
124
131
  }
125
- }
132
+ });
126
133
  const [hoveredDate, setHoveredDate] = React.useState(new Date(focusedDate));
127
134
  const focusRef = React.useRef<HTMLButtonElement>();
128
135
  const [hiddenMonthId] = React.useState(getUniqueId('hidden-month-span'));
@@ -131,21 +138,22 @@ export const CalendarMonth = ({
131
138
  const isValidated = (date: Date) => validators.every(validator => validator(date));
132
139
  const focusedDateValidated = isValidated(focusedDate);
133
140
  useEffect(() => {
134
- if (!(isValidDate(dateProp) && isSameDate(focusedDate, dateProp))) {
141
+ if (isValidDate(dateProp) && !isSameDate(focusedDate, dateProp)) {
135
142
  setFocusedDate(dateProp);
143
+ } else if (!dateProp) {
144
+ setFocusedDate(today);
136
145
  }
137
146
  }, [dateProp]);
138
147
 
139
148
  useEffect(() => {
140
- // When using header controls don't move focus
141
- if (shouldFocus) {
142
- if (focusRef.current && focusedDateValidated) {
143
- focusRef.current.focus();
144
- }
149
+ // Calendar month should not be focused on page load
150
+ // Datepicker should place focus in calendar month when opened
151
+ if ((shouldFocus || isDateFocused) && focusedDateValidated && focusRef.current) {
152
+ focusRef.current.focus();
145
153
  } else {
146
154
  setShouldFocus(true);
147
155
  }
148
- }, [focusedDate]);
156
+ }, [focusedDate, isDateFocused, focusedDateValidated, focusRef]);
149
157
 
150
158
  const onMonthClick = (newDate: Date) => {
151
159
  setFocusedDate(newDate);
@@ -8,6 +8,7 @@ import { InputGroup } from '../InputGroup/InputGroup';
8
8
  import OutlinedCalendarAltIcon from '@patternfly/react-icons/dist/esm/icons/outlined-calendar-alt-icon';
9
9
  import { CalendarMonth, CalendarFormat, isValidDate } from '../CalendarMonth';
10
10
  import { useImperativeHandle } from 'react';
11
+ import { KeyTypes } from '../../helpers';
11
12
 
12
13
  export interface DatePickerProps
13
14
  extends CalendarFormat,
@@ -36,8 +37,13 @@ export interface DatePickerProps
36
37
  helperText?: React.ReactNode;
37
38
  /** Aria label for the button to open the date picker */
38
39
  buttonAriaLabel?: string;
39
- /** The element to append the popover to */
40
- appendTo?: HTMLElement | ((ref?: HTMLElement) => HTMLElement);
40
+ /** The container to append the menu to. Defaults to 'parent'.
41
+ * If your menu is being cut off you can append it to an element higher up the DOM tree.
42
+ * Some examples:
43
+ * menuAppendTo={() => document.body}
44
+ * menuAppendTo={document.getElementById('target')}
45
+ */
46
+ appendTo?: HTMLElement | ((ref?: HTMLElement) => HTMLElement) | 'parent';
41
47
  /** Props to pass to the Popover */
42
48
  popoverProps?: Omit<PopoverProps, 'appendTo'>;
43
49
  /** Functions that returns an error message if a date is invalid */
@@ -49,8 +55,13 @@ export interface DatePickerProps
49
55
  export interface DatePickerRef {
50
56
  /** Sets the calendar open status */
51
57
  setCalendarOpen: (isOpen: boolean) => void;
52
- /** Toggles the calendar open status */
53
- toggleCalendar: () => void;
58
+ /** Toggles the calendar open status. If no parameters are passed, the calendar will simply toggle its open status.
59
+ * If the isOpen parameter is passed, that will set the calendar open status to the value of the isOpen parameter.
60
+ * If the eventKey parameter is set to 'Escape', that will invoke the date pickers onEscapePress event to toggle the
61
+ * correct control appropriately. */
62
+ toggleCalendar: (isOpen?: boolean, eventKey?: string) => void;
63
+ /** Current calendar open status */
64
+ isCalendarOpen: boolean;
54
65
  }
55
66
 
56
67
  export const yyyyMMddFormat = (date: Date) =>
@@ -74,7 +85,7 @@ const DatePickerBase = (
74
85
  onBlur = (): any => undefined,
75
86
  invalidFormatText = 'Invalid date',
76
87
  helperText,
77
- appendTo,
88
+ appendTo = 'parent',
78
89
  popoverProps,
79
90
  monthFormat,
80
91
  weekdayFormat,
@@ -98,6 +109,7 @@ const DatePickerBase = (
98
109
  const widthChars = React.useMemo(() => Math.max(dateFormat(new Date()).length, placeholder.length), [dateFormat]);
99
110
  const style = { '--pf-c-date-picker__input--c-form-control--width-chars': widthChars, ...styleProps };
100
111
  const buttonRef = React.useRef<HTMLButtonElement>();
112
+ const datePickerWrapperRef = React.useRef<HTMLDivElement>();
101
113
 
102
114
  React.useEffect(() => {
103
115
  setValue(valueProp);
@@ -159,13 +171,21 @@ const DatePickerBase = (
159
171
  ref,
160
172
  () => ({
161
173
  setCalendarOpen: (isOpen: boolean) => setPopoverOpen(isOpen),
162
- toggleCalendar: () => setPopoverOpen(prev => !prev)
174
+ toggleCalendar: (setOpen?: boolean, eventKey?: string) => {
175
+ if (eventKey === KeyTypes.Escape && popoverOpen && !selectOpen) {
176
+ setPopoverOpen(prev => (setOpen !== undefined ? setOpen : !prev));
177
+ }
178
+ },
179
+ isCalendarOpen: popoverOpen
163
180
  }),
164
- [setPopoverOpen]
181
+ [setPopoverOpen, popoverOpen, selectOpen]
165
182
  );
166
183
 
184
+ const getParentElement = () =>
185
+ datePickerWrapperRef && datePickerWrapperRef.current ? datePickerWrapperRef.current : null;
186
+
167
187
  return (
168
- <div className={css(styles.datePicker, className)} style={style} {...props}>
188
+ <div className={css(styles.datePicker, className)} ref={datePickerWrapperRef} style={style} {...props}>
169
189
  <Popover
170
190
  position="bottom"
171
191
  bodyContent={
@@ -182,14 +202,16 @@ const DatePickerBase = (
182
202
  dayFormat={dayFormat}
183
203
  weekStart={weekStart}
184
204
  rangeStart={rangeStart}
205
+ isDateFocused
185
206
  />
186
207
  }
187
208
  showClose={false}
188
209
  isVisible={popoverOpen}
189
210
  shouldClose={(_1, _2, event) => {
190
211
  event = event as KeyboardEvent;
191
- // Let the select menu close
192
- if (event.keyCode && event.keyCode === 27 && selectOpen) {
212
+ if (event.key === KeyTypes.Escape && selectOpen) {
213
+ event.stopPropagation();
214
+ setSelectOpen(false);
193
215
  return false;
194
216
  }
195
217
  // Let our button handle toggling
@@ -197,12 +219,15 @@ const DatePickerBase = (
197
219
  return false;
198
220
  }
199
221
  setPopoverOpen(false);
222
+ if (event.key === KeyTypes.Escape && popoverOpen) {
223
+ event.stopPropagation();
224
+ }
200
225
  return true;
201
226
  }}
202
227
  withFocusTrap
203
228
  hasNoPadding
204
229
  hasAutoWidth
205
- appendTo={appendTo}
230
+ appendTo={appendTo === 'parent' ? getParentElement() : appendTo}
206
231
  {...popoverProps}
207
232
  >
208
233
  <div className={styles.datePickerInput}>
@@ -110,15 +110,15 @@ export const Label: React.FunctionComponent<LabelProps> = ({
110
110
  const editableInputRef = React.useRef<HTMLInputElement>();
111
111
 
112
112
  React.useEffect(() => {
113
- document.addEventListener('click', onDocClick);
113
+ document.addEventListener('mousedown', onDocMouseDown);
114
114
  document.addEventListener('keydown', onKeyDown);
115
115
  return () => {
116
- document.removeEventListener('click', onDocClick);
116
+ document.removeEventListener('mousedown', onDocMouseDown);
117
117
  document.removeEventListener('keydown', onKeyDown);
118
118
  };
119
119
  });
120
120
 
121
- const onDocClick = (event: MouseEvent) => {
121
+ const onDocMouseDown = (event: MouseEvent) => {
122
122
  if (
123
123
  isEditableActive &&
124
124
  editableInputRef &&
@@ -58,6 +58,8 @@ export interface LabelGroupProps extends React.HTMLProps<HTMLUListElement> {
58
58
  hasEditableTextArea?: boolean;
59
59
  /** @beta Additional props passed to the editable textarea. */
60
60
  editableTextAreaProps?: any;
61
+ /** @beta Control for adding new labels */
62
+ addLabelControl?: React.ReactNode;
61
63
  }
62
64
 
63
65
  interface LabelGroupState {
@@ -149,6 +151,7 @@ export class LabelGroup extends React.Component<LabelGroupProps, LabelGroupState
149
151
  isEditable,
150
152
  hasEditableTextArea,
151
153
  editableTextAreaProps,
154
+ addLabelControl,
152
155
  /* eslint-enable @typescript-eslint/no-unused-vars */
153
156
  ...rest
154
157
  } = this.props;
@@ -189,6 +192,7 @@ export class LabelGroup extends React.Component<LabelGroupProps, LabelGroupState
189
192
  </Label>
190
193
  </li>
191
194
  )}
195
+ {addLabelControl && <li className={css(styles.labelGroupListItem)}>{addLabelControl}</li>}
192
196
  {isEditable && hasEditableTextArea && (
193
197
  <li className={css(styles.labelGroupListItem, styles.modifiers.textarea)}>
194
198
  <textarea className={css(styles.labelGroupTextarea)} rows={1} tabIndex={0} {...editableTextAreaProps} />
@@ -228,7 +232,7 @@ export class LabelGroup extends React.Component<LabelGroupProps, LabelGroupState
228
232
  );
229
233
  };
230
234
 
231
- return numChildren === 0 ? null : (
235
+ return numChildren === 0 && addLabelControl === undefined ? null : (
232
236
  <GenerateId>{randomId => renderLabelGroup(this.props.id || randomId)}</GenerateId>
233
237
  );
234
238
  }
@@ -40,3 +40,10 @@ import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-i
40
40
 
41
41
  ```ts file="LabelGroupEditableLabels.tsx" isBeta
42
42
  ```
43
+
44
+ ### Editable labels with add button
45
+
46
+ For additional documentation that showcases adding a new label, see [label group demos](/components/label-group/react-demos).
47
+
48
+ ```ts file="LabelGroupEditableAdd.tsx"
49
+ ```
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { LabelGroup, Label } from '@patternfly/react-core';
3
+
4
+ export const LabelGroupEditableAdd: React.FunctionComponent = () => {
5
+ const [idIndex, setIdIndex] = React.useState<number>(3);
6
+ const [labels, setLabels] = React.useState<any>([
7
+ { name: 'Label 1', id: 0 },
8
+ { name: 'Label 2', id: 1 },
9
+ {
10
+ name: 'Label 3',
11
+ props: {
12
+ isEditable: true,
13
+ editableProps: {
14
+ 'aria-label': 'label editable text'
15
+ }
16
+ },
17
+ id: 2
18
+ }
19
+ ]);
20
+
21
+ const onClose = (labelId: string) => {
22
+ setLabels(labels.filter((l: any) => l.id !== labelId));
23
+ };
24
+
25
+ const onEdit = (nextText: string, index: number) => {
26
+ const copy = [...labels];
27
+ copy[index] = { name: nextText, props: labels[index].props, id: labels[index].id };
28
+ setLabels(copy);
29
+ };
30
+
31
+ const onAdd = () => {
32
+ setLabels([
33
+ {
34
+ name: 'New Label',
35
+ props: {
36
+ isEditable: true,
37
+ editableProps: {
38
+ 'aria-label': 'label editable text'
39
+ }
40
+ },
41
+ id: idIndex
42
+ },
43
+ ...labels
44
+ ]);
45
+ setIdIndex(idIndex + 1);
46
+ };
47
+
48
+ return (
49
+ <LabelGroup
50
+ categoryName="Label group 1"
51
+ numLabels={5}
52
+ isEditable
53
+ addLabelControl={
54
+ <Label color="blue" variant="outline" isOverflowLabel onClick={onAdd}>
55
+ Add label
56
+ </Label>
57
+ }
58
+ >
59
+ {labels.map((label, index) => (
60
+ <Label
61
+ key={`${label.name}-${index}`}
62
+ id={`${label.name}-${index}`}
63
+ color="blue"
64
+ onClose={() => onClose(label.id)}
65
+ onEditCancel={prevText => onEdit(prevText, index)}
66
+ onEditComplete={newText => onEdit(newText, index)}
67
+ {...label.props}
68
+ >
69
+ {label.name}
70
+ </Label>
71
+ ))}
72
+ </LabelGroup>
73
+ );
74
+ };
@@ -0,0 +1,138 @@
1
+ import React from 'react';
2
+ import { LabelGroup, Label, Menu, MenuContent, MenuList, MenuItem, Popper } from '@patternfly/react-core';
3
+
4
+ export const LabelGroupEditableAddDropdown: React.FunctionComponent = () => {
5
+ const toggleRef = React.useRef<HTMLDivElement>();
6
+ const menuRef = React.useRef<HTMLDivElement>();
7
+ const containerRef = React.useRef<HTMLDivElement>();
8
+
9
+ const [idIndex, setIdIndex] = React.useState<number>(3);
10
+ const [isOpen, setIsOpen] = React.useState<boolean>(false);
11
+ const [labels, setLabels] = React.useState<any>([
12
+ { name: 'Label 1', id: 0 },
13
+ { name: 'Label 2', id: 1 },
14
+ {
15
+ name: 'Label 3',
16
+ props: {
17
+ isEditable: true,
18
+ editableProps: {
19
+ 'aria-label': 'label editable text'
20
+ }
21
+ },
22
+ id: 2
23
+ }
24
+ ]);
25
+
26
+ const onClose = (labelId: string) => {
27
+ setLabels(labels.filter((l: any) => l.id !== labelId));
28
+ };
29
+
30
+ const onEdit = (nextText: string, index: number) => {
31
+ const copy = [...labels];
32
+ copy[index] = { name: nextText, props: labels[index].props, id: labels[index].id };
33
+ setLabels(copy);
34
+ };
35
+
36
+ const onAdd = (labelText: string) => {
37
+ setLabels([
38
+ {
39
+ name: labelText,
40
+ id: idIndex
41
+ },
42
+ ...labels
43
+ ]);
44
+ setIdIndex(idIndex + 1);
45
+ setIsOpen(!isOpen);
46
+ };
47
+
48
+ const handleMenuKeys = (event: KeyboardEvent) => {
49
+ if (isOpen && menuRef.current.contains(event.target as Node)) {
50
+ if (event.key === 'Escape' || event.key === 'Tab') {
51
+ setIsOpen(!isOpen);
52
+ toggleRef.current.focus();
53
+ }
54
+ }
55
+ };
56
+
57
+ const handleClickOutside = (event: MouseEvent) => {
58
+ if (
59
+ isOpen &&
60
+ !(menuRef.current.contains(event.target as Node) || toggleRef.current.contains(event.target as Node))
61
+ ) {
62
+ setIsOpen(false);
63
+ }
64
+ };
65
+
66
+ React.useEffect(() => {
67
+ window.addEventListener('keydown', handleMenuKeys);
68
+ window.addEventListener('click', handleClickOutside);
69
+ return () => {
70
+ window.removeEventListener('keydown', handleMenuKeys);
71
+ window.removeEventListener('click', handleClickOutside);
72
+ };
73
+ }, [isOpen, menuRef]);
74
+
75
+ const onToggleClick = () => {
76
+ setTimeout(() => {
77
+ if (menuRef.current) {
78
+ const firstElement = menuRef.current.querySelector('li > button:not(:disabled)');
79
+ firstElement && (firstElement as HTMLElement).focus();
80
+ }
81
+ }, 0);
82
+ setIsOpen(!isOpen);
83
+ };
84
+
85
+ const menu = (
86
+ <Menu ref={menuRef} onSelect={(_ev, itemId) => onAdd(itemId.toString())}>
87
+ <MenuContent>
88
+ <MenuList>
89
+ <MenuItem itemId="Label text 1">Label text 1</MenuItem>
90
+ <MenuItem itemId="Label text 2">Label text 2</MenuItem>
91
+ <MenuItem itemId="Label text 3">Label text 3</MenuItem>
92
+ <MenuItem itemId="Label text 4">Label text 4</MenuItem>
93
+ </MenuList>
94
+ </MenuContent>
95
+ </Menu>
96
+ );
97
+
98
+ const toggle = (
99
+ <div ref={toggleRef}>
100
+ <Label color="blue" variant="outline" isOverflowLabel onClick={onToggleClick}>
101
+ Add label
102
+ </Label>
103
+ </div>
104
+ );
105
+
106
+ return (
107
+ <div ref={containerRef}>
108
+ <LabelGroup
109
+ categoryName="Label group 1"
110
+ numLabels={5}
111
+ isEditable
112
+ addLabelControl={
113
+ <Popper
114
+ trigger={toggle}
115
+ popper={menu}
116
+ appendTo={containerRef.current}
117
+ isVisible={isOpen}
118
+ popperMatchesTriggerWidth={false}
119
+ />
120
+ }
121
+ >
122
+ {labels.map((label, index) => (
123
+ <Label
124
+ key={`${label.name}-${index}`}
125
+ id={`${label.name}-${index}`}
126
+ color="blue"
127
+ onClose={() => onClose(label.id)}
128
+ onEditCancel={prevText => onEdit(prevText, index)}
129
+ onEditComplete={newText => onEdit(newText, index)}
130
+ {...label.props}
131
+ >
132
+ {label.name}
133
+ </Label>
134
+ ))}
135
+ </LabelGroup>
136
+ </div>
137
+ );
138
+ };