carbon-react 144.2.2 → 144.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,8 @@ export interface DrawerProps extends Omit<TagProps, "data-component"> {
6
6
  animationDuration?: string;
7
7
  /** Specify an aria-label for the Drawer component */
8
8
  "aria-label"?: string;
9
+ /** Specify an aria-label for the Drawer sidebar */
10
+ sidebarAriaLabel?: string;
9
11
  /** Sets color of sidebar's background */
10
12
  backgroundColor?: string;
11
13
  children: React.ReactNode;
@@ -30,6 +32,6 @@ export interface DrawerProps extends Omit<TagProps, "data-component"> {
30
32
  /** Makes the footer of the drawer sticky. Footer prop must also be set. */
31
33
  stickyFooter?: boolean;
32
34
  }
33
- export declare const Drawer: ({ "aria-label": ariaLabel, "data-element": dataElement, "data-role": dataRole, defaultExpanded, expanded, onChange, children, expandedWidth, sidebar, animationDuration, backgroundColor, title, footer, showControls, height, stickyHeader, stickyFooter, }: DrawerProps) => React.JSX.Element;
35
+ export declare const Drawer: ({ "aria-label": ariaLabel, sidebarAriaLabel, "data-element": dataElement, "data-role": dataRole, defaultExpanded, expanded, onChange, children, expandedWidth, sidebar, animationDuration, backgroundColor, title, footer, showControls, height, stickyHeader, stickyFooter, }: DrawerProps) => React.JSX.Element;
34
36
  export { DrawerSidebarContext };
35
37
  export default Drawer;
@@ -9,6 +9,7 @@ import StickyFooter from "../../__internal__/sticky-footer";
9
9
  import DrawerSidebarContext from "./__internal__/drawer-sidebar.context";
10
10
  export const Drawer = ({
11
11
  "aria-label": ariaLabel,
12
+ sidebarAriaLabel,
12
13
  "data-element": dataElement,
13
14
  "data-role": dataRole = "drawer",
14
15
  defaultExpanded = true,
@@ -91,6 +92,7 @@ export const Drawer = ({
91
92
  }, [toggleAnimation, isExpanded, onChange]);
92
93
  const guid = useRef(createGuid());
93
94
  const sidebarId = `DrawerSidebar_${guid.current}`;
95
+ const titleId = `DrawerTitle_${guid.current}`;
94
96
  const getClassNames = useCallback(() => {
95
97
  const classes = [isExpanded ? "open" : "closed"];
96
98
  if (isOpening) {
@@ -128,15 +130,20 @@ export const Drawer = ({
128
130
  ref: drawerSidebarContentRef,
129
131
  backgroundColor: backgroundColor,
130
132
  "data-element": "drawer-content",
131
- "data-role": "drawer-content"
133
+ "data-role": "drawer-content",
134
+ "aria-label": sidebarAriaLabel,
135
+ "aria-labelledby": title ? titleId : undefined
132
136
  }, stickyHeader && /*#__PURE__*/React.createElement(StyledSidebarHeader, {
133
137
  "data-role": "drawer-sidebar-header",
134
138
  isExpanded: isExpanded
135
- }, title && /*#__PURE__*/React.createElement(StyledSidebarTitle, null, title), getControls()), !stickyHeader && /*#__PURE__*/React.createElement(React.Fragment, null, title && /*#__PURE__*/React.createElement(StyledSidebarTitle, null, title), getControls()), /*#__PURE__*/React.createElement(StyledDrawerSidebar, {
139
+ }, title && /*#__PURE__*/React.createElement(StyledSidebarTitle, {
140
+ id: titleId
141
+ }, title), getControls()), !stickyHeader && /*#__PURE__*/React.createElement(React.Fragment, null, title && /*#__PURE__*/React.createElement(StyledSidebarTitle, {
142
+ id: titleId
143
+ }, title), getControls()), /*#__PURE__*/React.createElement(StyledDrawerSidebar, {
136
144
  hasControls: !!showControls,
137
145
  id: sidebarId,
138
146
  isExpanded: isExpanded,
139
- role: "navigation",
140
147
  overflowY: isExpanded ? "auto" : undefined,
141
148
  scrollVariant: "light",
142
149
  ref: scrollableContentRef
@@ -14,7 +14,7 @@ interface StyledDrawerContentProps {
14
14
  backgroundColor?: string;
15
15
  expandedWidth: string;
16
16
  }
17
- declare const StyledDrawerContent: import("styled-components").StyledComponent<"div", any, StyledDrawerContentProps, never>;
17
+ declare const StyledDrawerContent: import("styled-components").StyledComponent<"aside", any, StyledDrawerContentProps, never>;
18
18
  interface StyledSidebarToggleButtonProps {
19
19
  animationDuration?: string;
20
20
  isExpanded?: boolean;
@@ -89,7 +89,7 @@ const buttonClose = () => keyframes`
89
89
  80% {float: right;}
90
90
  100% {float: left;}
91
91
  `;
92
- const StyledDrawerContent = styled.div`
92
+ const StyledDrawerContent = styled.aside`
93
93
  display: flex;
94
94
  flex-direction: column;
95
95
  min-width: ${defaultExpandedWidth};
@@ -380,7 +380,7 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
380
380
  }
381
381
  }), ...(flipEnabled ? [flip({
382
382
  fallbackStrategy: "initialPlacement"
383
- })] : [])], [listWidth, flipEnabled]);
383
+ })] : /* istanbul ignore next: covered by Playwright tests for reliable positioning in a real browser */[])], [listWidth, flipEnabled]);
384
384
  const loader = isLoading ? /*#__PURE__*/React.createElement(StyledSelectLoaderContainer, {
385
385
  key: "loader"
386
386
  }, /*#__PURE__*/React.createElement(Loader, null)) : undefined;
@@ -63,18 +63,16 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
63
63
  const accessibilityLabelId = useRef(guid());
64
64
  const containerRef = useRef(null);
65
65
  const listboxRef = useRef(null);
66
- const isInputFocused = useRef(false);
67
66
  const isClickTriggeredBySelect = useRef(false);
68
67
  const isMouseDownReported = useRef(false);
69
68
  const isMouseDownOnInput = useRef(false);
70
- const isOpenedByFocus = useRef(false);
69
+ const [isOpenedByFocus, setIsOpenedByFocus] = useState(false);
71
70
  const isControlled = useRef(value !== undefined);
72
71
  const [textboxRef, setTextboxRef] = useState();
73
- const [isOpen, setOpenState] = useState(false);
72
+ const [open, setOpen] = useState(false);
74
73
  const [textValue, setTextValue] = useState("");
75
74
  const [selectedValue, setSelectedValue] = useState(value || defaultValue || []);
76
75
  const [highlightedValue, setHighlightedValue] = useState("");
77
- const [filterText, setFilterText] = useState("");
78
76
  const [placeholderOverride, setPlaceholderOverride] = useState();
79
77
  const inputId = useRef(id || guid());
80
78
  const {
@@ -83,21 +81,12 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
83
81
  id: inputId.current,
84
82
  label
85
83
  });
86
- const focusTimer = useRef(null);
87
84
  const actualValue = isControlled.current ? value : selectedValue;
88
85
  const componentIsUncontrolled = !isControlled || !onChange && defaultValue;
89
86
  if (!deprecateUncontrolledWarnTriggered && componentIsUncontrolled) {
90
87
  deprecateUncontrolledWarnTriggered = true;
91
88
  Logger.deprecate("Uncontrolled behaviour in `Multi Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
92
89
  }
93
- const setOpen = useCallback(() => {
94
- setOpenState(isAlreadyOpen => {
95
- if (!isAlreadyOpen && onOpen) {
96
- onOpen();
97
- }
98
- return true;
99
- });
100
- }, [onOpen]);
101
90
  const createCustomEvent = useCallback((newValue, selectionConfirmed) => {
102
91
  const customEvent = {
103
92
  target: {
@@ -144,10 +133,12 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
144
133
  if (match) {
145
134
  setHighlightedValue(match.props.value);
146
135
  }
147
- setFilterText(newValue);
148
136
  setTextValue(newValue);
149
- setOpen();
150
- }, [children, setOpen]);
137
+ if (!open) {
138
+ onOpen?.();
139
+ }
140
+ setOpen(true);
141
+ }, [children, open, onOpen]);
151
142
  const removeSelectedValue = useCallback(index => {
152
143
  isClickTriggeredBySelect.current = true;
153
144
  updateValue(previousValue => {
@@ -164,21 +155,21 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
164
155
  key
165
156
  } = event;
166
157
  const isDeleteKey = key === "Backspace" || key === "Delete";
167
- if (onKeyDown) {
168
- onKeyDown(event);
169
- }
158
+ onKeyDown?.(event);
170
159
  if (readOnly) {
171
160
  return;
172
161
  }
173
162
  if (!event.defaultPrevented && isNavigationKey(key)) {
174
163
  event.preventDefault();
175
- setOpen();
164
+ if (!open) {
165
+ onOpen?.();
166
+ }
167
+ setOpen(true);
176
168
  }
177
- if (isDeleteKey && (filterText === "" || textValue === "")) {
169
+ if (isDeleteKey && textValue === "") {
178
170
  removeSelectedValue(-1);
179
171
  }
180
- // eslint-disable-next-line react-hooks/exhaustive-deps
181
- }, [onKeyDown, readOnly, filterText, textValue, setOpen, removeSelectedValue]);
172
+ }, [onKeyDown, readOnly, textValue, open, onOpen, removeSelectedValue]);
182
173
  const accessibilityLabel = useMemo(() => {
183
174
  return actualValue && actualValue.length ? React.Children.map(children, child => {
184
175
  return /*#__PURE__*/React.isValidElement(child) && actualValue.includes(child.props.value) ? child.props.text : false;
@@ -188,19 +179,18 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
188
179
  }, [children, actualValue]);
189
180
  const handleGlobalClick = useCallback(event => {
190
181
  isMouseDownReported.current = false;
191
- if (!isOpen) {
182
+ if (!open) {
192
183
  return;
193
184
  }
194
185
  const notInContainer = containerRef.current && !containerRef.current.contains(event.target);
195
186
  const notInList = listboxRef.current && !listboxRef.current.contains(event.target);
196
187
  if (notInContainer && notInList && !isClickTriggeredBySelect.current) {
197
188
  setTextValue("");
198
- setFilterText("");
199
189
  setHighlightedValue("");
200
- setOpenState(false);
190
+ setOpen(false);
201
191
  }
202
192
  isClickTriggeredBySelect.current = false;
203
- }, [isOpen]);
193
+ }, [open]);
204
194
  const mapValuesToPills = useMemo(() => {
205
195
  const canDelete = !disabled && !readOnly;
206
196
  let matchingOptionValue;
@@ -261,99 +251,53 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
261
251
  const onFilterChange = useStableCallback(onFilterChangeProp);
262
252
  const isFirstRender = useRef(true);
263
253
  useEffect(() => {
264
- if (onFilterChange && !isFirstRender.current) {
265
- onFilterChange(filterText);
254
+ if (!isFirstRender.current) {
255
+ onFilterChange?.(textValue);
266
256
  }
267
- }, [onFilterChange, filterText]);
257
+ }, [onFilterChange, textValue]);
268
258
  useEffect(() => {
269
259
  isFirstRender.current = false;
270
260
  }, []);
271
261
  function handleTextboxClick(event) {
272
262
  isMouseDownReported.current = false;
273
- if (onClick) {
274
- onClick(event);
263
+ onClick?.(event);
264
+ if (openOnFocus && isOpenedByFocus) {
265
+ setIsOpenedByFocus(false);
266
+ return;
275
267
  }
276
- if (!openOnFocus || openOnFocus && !isOpenedByFocus.current) {
277
- if (isOpen) {
278
- setFilterText("");
279
- setOpenState(false);
280
- return;
281
- }
282
- onOpen?.();
283
- setOpenState(true);
268
+ if (open) {
269
+ setTextValue("");
270
+ setOpen(false);
284
271
  } else {
285
- isOpenedByFocus.current = false;
286
- }
287
- }
288
- function handleDropdownIconClick(event) {
289
- isMouseDownReported.current = false;
290
- if (onClick) {
291
- onClick(event);
272
+ onOpen?.();
273
+ setOpen(true);
292
274
  }
293
- setOpenState(isAlreadyOpen => {
294
- if (isAlreadyOpen) {
295
- setFilterText("");
296
- return false;
297
- }
298
- if (onOpen) {
299
- onOpen();
300
- }
301
- return true;
302
- });
303
275
  }
304
276
  function handleTextboxBlur(event) {
305
277
  isMouseDownOnInput.current = false;
306
278
  if (isMouseDownReported.current) {
307
279
  return;
308
280
  }
309
- isInputFocused.current = false;
310
- if (onBlur) {
311
- onBlur(event);
312
- }
281
+ onBlur?.(event);
282
+ setTextValue("");
283
+ setOpen(false);
313
284
  }
314
- function handleTextboxMouseDown(event) {
285
+ function handleTextboxMouseDown() {
315
286
  isMouseDownReported.current = true;
316
- if (event.target.dataset.element === "input") {
317
- isMouseDownOnInput.current = true;
318
- }
287
+ isMouseDownOnInput.current = true;
319
288
  }
320
289
  function handleListMouseDown() {
321
290
  isMouseDownReported.current = true;
322
291
  }
323
292
  function handleTextboxFocus(event) {
324
- const triggerFocus = () => onFocus?.(event);
293
+ onFocus?.(event);
325
294
  if (openOnFocus) {
326
- if (focusTimer.current) {
327
- clearTimeout(focusTimer.current);
295
+ if (open) return;
296
+ onOpen?.();
297
+ if (isMouseDownOnInput.current) {
298
+ setIsOpenedByFocus(true);
328
299
  }
329
-
330
- // we need to use a timeout here as there is a race condition when rendered in a modal
331
- // whereby the select list isn't visible when the select is auto focused straight away
332
- focusTimer.current = setTimeout(() => {
333
- setOpenState(isAlreadyOpen => {
334
- if (isAlreadyOpen) {
335
- return true;
336
- }
337
- if (onOpen) {
338
- onOpen();
339
- }
340
- if (onFocus && !isInputFocused.current) {
341
- triggerFocus();
342
- isInputFocused.current = true;
343
- }
344
- if (isMouseDownReported.current && !isMouseDownOnInput.current) {
345
- isOpenedByFocus.current = false;
346
- return false;
347
- }
348
- if (isMouseDownOnInput.current) {
349
- isOpenedByFocus.current = true;
350
- }
351
- return true;
352
- });
353
- });
354
- } else if (onFocus && !isInputFocused.current) {
355
- triggerFocus();
356
- isInputFocused.current = true;
300
+ setOpen(true);
357
301
  }
358
302
  }
359
303
  const onSelectOption = useCallback(optionData => {
@@ -383,8 +327,8 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
383
327
  }, selectionConfirmed);
384
328
  }, [textboxRef, actualValue, updateValue]);
385
329
  const onSelectListClose = useCallback(() => {
386
- setOpenState(false);
387
- setFilterText("");
330
+ setOpen(false);
331
+ setTextValue("");
388
332
  }, []);
389
333
  const assignInput = useCallback(element => {
390
334
  if (!element) return;
@@ -411,8 +355,6 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
411
355
  onMouseDown: handleTextboxMouseDown,
412
356
  onFocus: handleTextboxFocus,
413
357
  onBlur: handleTextboxBlur,
414
- iconOnClick: handleDropdownIconClick,
415
- iconOnMouseDown: handleTextboxMouseDown,
416
358
  onKeyDown: handleTextboxKeydown,
417
359
  onChange: handleTextboxChange,
418
360
  tooltipPosition,
@@ -441,7 +383,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
441
383
  onSelect: onSelectOption,
442
384
  onSelectListClose: onSelectListClose,
443
385
  onMouseDown: handleListMouseDown,
444
- filterText: filterText.trim(),
386
+ filterText: textValue.trim(),
445
387
  highlightedValue: highlightedValue,
446
388
  noResultsMessage: noResultsMessage,
447
389
  isLoading: isLoading,
@@ -451,7 +393,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
451
393
  listMaxHeight: listMaxHeight,
452
394
  flipEnabled: flipEnabled,
453
395
  multiselectValues: actualValue,
454
- isOpen: isOpen,
396
+ isOpen: open,
455
397
  enableVirtualScroll: enableVirtualScroll,
456
398
  virtualScrollOverscan: virtualScrollOverscan,
457
399
  listWidth: listWidth
@@ -465,7 +407,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
465
407
  "data-component": dataComponent,
466
408
  "data-role": dataRole,
467
409
  "data-element": dataElement,
468
- isOpen: isOpen
410
+ isOpen: open
469
411
  }, marginProps), /*#__PURE__*/React.createElement("div", {
470
412
  ref: containerRef
471
413
  }, /*#__PURE__*/React.createElement(StyledAccessibilityLabelContainer, {
@@ -479,7 +421,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
479
421
  ariaLabel: ariaLabel,
480
422
  ariaLabelledby: ariaLabelledby,
481
423
  hasTextCursor: true,
482
- isOpen: isOpen,
424
+ isOpen: open,
483
425
  labelId: labelId
484
426
  }, getTextboxProps()))), selectList);
485
427
  });
@@ -6,6 +6,8 @@ export interface DrawerProps extends Omit<TagProps, "data-component"> {
6
6
  animationDuration?: string;
7
7
  /** Specify an aria-label for the Drawer component */
8
8
  "aria-label"?: string;
9
+ /** Specify an aria-label for the Drawer sidebar */
10
+ sidebarAriaLabel?: string;
9
11
  /** Sets color of sidebar's background */
10
12
  backgroundColor?: string;
11
13
  children: React.ReactNode;
@@ -30,6 +32,6 @@ export interface DrawerProps extends Omit<TagProps, "data-component"> {
30
32
  /** Makes the footer of the drawer sticky. Footer prop must also be set. */
31
33
  stickyFooter?: boolean;
32
34
  }
33
- export declare const Drawer: ({ "aria-label": ariaLabel, "data-element": dataElement, "data-role": dataRole, defaultExpanded, expanded, onChange, children, expandedWidth, sidebar, animationDuration, backgroundColor, title, footer, showControls, height, stickyHeader, stickyFooter, }: DrawerProps) => React.JSX.Element;
35
+ export declare const Drawer: ({ "aria-label": ariaLabel, sidebarAriaLabel, "data-element": dataElement, "data-role": dataRole, defaultExpanded, expanded, onChange, children, expandedWidth, sidebar, animationDuration, backgroundColor, title, footer, showControls, height, stickyHeader, stickyFooter, }: DrawerProps) => React.JSX.Element;
34
36
  export { DrawerSidebarContext };
35
37
  export default Drawer;
@@ -25,6 +25,7 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
25
25
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
26
26
  const Drawer = ({
27
27
  "aria-label": ariaLabel,
28
+ sidebarAriaLabel,
28
29
  "data-element": dataElement,
29
30
  "data-role": dataRole = "drawer",
30
31
  defaultExpanded = true,
@@ -107,6 +108,7 @@ const Drawer = ({
107
108
  }, [toggleAnimation, isExpanded, onChange]);
108
109
  const guid = (0, _react.useRef)((0, _guid.default)());
109
110
  const sidebarId = `DrawerSidebar_${guid.current}`;
111
+ const titleId = `DrawerTitle_${guid.current}`;
110
112
  const getClassNames = (0, _react.useCallback)(() => {
111
113
  const classes = [isExpanded ? "open" : "closed"];
112
114
  if (isOpening) {
@@ -144,15 +146,20 @@ const Drawer = ({
144
146
  ref: drawerSidebarContentRef,
145
147
  backgroundColor: backgroundColor,
146
148
  "data-element": "drawer-content",
147
- "data-role": "drawer-content"
149
+ "data-role": "drawer-content",
150
+ "aria-label": sidebarAriaLabel,
151
+ "aria-labelledby": title ? titleId : undefined
148
152
  }, stickyHeader && /*#__PURE__*/_react.default.createElement(_drawer.StyledSidebarHeader, {
149
153
  "data-role": "drawer-sidebar-header",
150
154
  isExpanded: isExpanded
151
- }, title && /*#__PURE__*/_react.default.createElement(_drawer.StyledSidebarTitle, null, title), getControls()), !stickyHeader && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, title && /*#__PURE__*/_react.default.createElement(_drawer.StyledSidebarTitle, null, title), getControls()), /*#__PURE__*/_react.default.createElement(_drawer.StyledDrawerSidebar, {
155
+ }, title && /*#__PURE__*/_react.default.createElement(_drawer.StyledSidebarTitle, {
156
+ id: titleId
157
+ }, title), getControls()), !stickyHeader && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, title && /*#__PURE__*/_react.default.createElement(_drawer.StyledSidebarTitle, {
158
+ id: titleId
159
+ }, title), getControls()), /*#__PURE__*/_react.default.createElement(_drawer.StyledDrawerSidebar, {
152
160
  hasControls: !!showControls,
153
161
  id: sidebarId,
154
162
  isExpanded: isExpanded,
155
- role: "navigation",
156
163
  overflowY: isExpanded ? "auto" : undefined,
157
164
  scrollVariant: "light",
158
165
  ref: scrollableContentRef
@@ -14,7 +14,7 @@ interface StyledDrawerContentProps {
14
14
  backgroundColor?: string;
15
15
  expandedWidth: string;
16
16
  }
17
- declare const StyledDrawerContent: import("styled-components").StyledComponent<"div", any, StyledDrawerContentProps, never>;
17
+ declare const StyledDrawerContent: import("styled-components").StyledComponent<"aside", any, StyledDrawerContentProps, never>;
18
18
  interface StyledSidebarToggleButtonProps {
19
19
  animationDuration?: string;
20
20
  isExpanded?: boolean;
@@ -98,7 +98,7 @@ const buttonClose = () => (0, _styledComponents.keyframes)`
98
98
  80% {float: right;}
99
99
  100% {float: left;}
100
100
  `;
101
- const StyledDrawerContent = exports.StyledDrawerContent = _styledComponents.default.div`
101
+ const StyledDrawerContent = exports.StyledDrawerContent = _styledComponents.default.aside`
102
102
  display: flex;
103
103
  flex-direction: column;
104
104
  min-width: ${defaultExpandedWidth};
@@ -389,7 +389,7 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
389
389
  }
390
390
  }), ...(flipEnabled ? [(0, _dom.flip)({
391
391
  fallbackStrategy: "initialPlacement"
392
- })] : [])], [listWidth, flipEnabled]);
392
+ })] : /* istanbul ignore next: covered by Playwright tests for reliable positioning in a real browser */[])], [listWidth, flipEnabled]);
393
393
  const loader = isLoading ? /*#__PURE__*/_react.default.createElement(_selectList.StyledSelectLoaderContainer, {
394
394
  key: "loader"
395
395
  }, /*#__PURE__*/_react.default.createElement(_loader.default, null)) : undefined;
@@ -72,18 +72,16 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
72
72
  const accessibilityLabelId = (0, _react.useRef)((0, _guid.default)());
73
73
  const containerRef = (0, _react.useRef)(null);
74
74
  const listboxRef = (0, _react.useRef)(null);
75
- const isInputFocused = (0, _react.useRef)(false);
76
75
  const isClickTriggeredBySelect = (0, _react.useRef)(false);
77
76
  const isMouseDownReported = (0, _react.useRef)(false);
78
77
  const isMouseDownOnInput = (0, _react.useRef)(false);
79
- const isOpenedByFocus = (0, _react.useRef)(false);
78
+ const [isOpenedByFocus, setIsOpenedByFocus] = (0, _react.useState)(false);
80
79
  const isControlled = (0, _react.useRef)(value !== undefined);
81
80
  const [textboxRef, setTextboxRef] = (0, _react.useState)();
82
- const [isOpen, setOpenState] = (0, _react.useState)(false);
81
+ const [open, setOpen] = (0, _react.useState)(false);
83
82
  const [textValue, setTextValue] = (0, _react.useState)("");
84
83
  const [selectedValue, setSelectedValue] = (0, _react.useState)(value || defaultValue || []);
85
84
  const [highlightedValue, setHighlightedValue] = (0, _react.useState)("");
86
- const [filterText, setFilterText] = (0, _react.useState)("");
87
85
  const [placeholderOverride, setPlaceholderOverride] = (0, _react.useState)();
88
86
  const inputId = (0, _react.useRef)(id || (0, _guid.default)());
89
87
  const {
@@ -92,21 +90,12 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
92
90
  id: inputId.current,
93
91
  label
94
92
  });
95
- const focusTimer = (0, _react.useRef)(null);
96
93
  const actualValue = isControlled.current ? value : selectedValue;
97
94
  const componentIsUncontrolled = !isControlled || !onChange && defaultValue;
98
95
  if (!deprecateUncontrolledWarnTriggered && componentIsUncontrolled) {
99
96
  deprecateUncontrolledWarnTriggered = true;
100
97
  _logger.default.deprecate("Uncontrolled behaviour in `Multi Select` is deprecated and support will soon be removed. Please make sure all your inputs are controlled.");
101
98
  }
102
- const setOpen = (0, _react.useCallback)(() => {
103
- setOpenState(isAlreadyOpen => {
104
- if (!isAlreadyOpen && onOpen) {
105
- onOpen();
106
- }
107
- return true;
108
- });
109
- }, [onOpen]);
110
99
  const createCustomEvent = (0, _react.useCallback)((newValue, selectionConfirmed) => {
111
100
  const customEvent = {
112
101
  target: {
@@ -153,10 +142,12 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
153
142
  if (match) {
154
143
  setHighlightedValue(match.props.value);
155
144
  }
156
- setFilterText(newValue);
157
145
  setTextValue(newValue);
158
- setOpen();
159
- }, [children, setOpen]);
146
+ if (!open) {
147
+ onOpen?.();
148
+ }
149
+ setOpen(true);
150
+ }, [children, open, onOpen]);
160
151
  const removeSelectedValue = (0, _react.useCallback)(index => {
161
152
  isClickTriggeredBySelect.current = true;
162
153
  updateValue(previousValue => {
@@ -173,21 +164,21 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
173
164
  key
174
165
  } = event;
175
166
  const isDeleteKey = key === "Backspace" || key === "Delete";
176
- if (onKeyDown) {
177
- onKeyDown(event);
178
- }
167
+ onKeyDown?.(event);
179
168
  if (readOnly) {
180
169
  return;
181
170
  }
182
171
  if (!event.defaultPrevented && (0, _isNavigationKey.default)(key)) {
183
172
  event.preventDefault();
184
- setOpen();
173
+ if (!open) {
174
+ onOpen?.();
175
+ }
176
+ setOpen(true);
185
177
  }
186
- if (isDeleteKey && (filterText === "" || textValue === "")) {
178
+ if (isDeleteKey && textValue === "") {
187
179
  removeSelectedValue(-1);
188
180
  }
189
- // eslint-disable-next-line react-hooks/exhaustive-deps
190
- }, [onKeyDown, readOnly, filterText, textValue, setOpen, removeSelectedValue]);
181
+ }, [onKeyDown, readOnly, textValue, open, onOpen, removeSelectedValue]);
191
182
  const accessibilityLabel = (0, _react.useMemo)(() => {
192
183
  return actualValue && actualValue.length ? _react.default.Children.map(children, child => {
193
184
  return /*#__PURE__*/_react.default.isValidElement(child) && actualValue.includes(child.props.value) ? child.props.text : false;
@@ -197,19 +188,18 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
197
188
  }, [children, actualValue]);
198
189
  const handleGlobalClick = (0, _react.useCallback)(event => {
199
190
  isMouseDownReported.current = false;
200
- if (!isOpen) {
191
+ if (!open) {
201
192
  return;
202
193
  }
203
194
  const notInContainer = containerRef.current && !containerRef.current.contains(event.target);
204
195
  const notInList = listboxRef.current && !listboxRef.current.contains(event.target);
205
196
  if (notInContainer && notInList && !isClickTriggeredBySelect.current) {
206
197
  setTextValue("");
207
- setFilterText("");
208
198
  setHighlightedValue("");
209
- setOpenState(false);
199
+ setOpen(false);
210
200
  }
211
201
  isClickTriggeredBySelect.current = false;
212
- }, [isOpen]);
202
+ }, [open]);
213
203
  const mapValuesToPills = (0, _react.useMemo)(() => {
214
204
  const canDelete = !disabled && !readOnly;
215
205
  let matchingOptionValue;
@@ -270,99 +260,53 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
270
260
  const onFilterChange = (0, _useStableCallback.default)(onFilterChangeProp);
271
261
  const isFirstRender = (0, _react.useRef)(true);
272
262
  (0, _react.useEffect)(() => {
273
- if (onFilterChange && !isFirstRender.current) {
274
- onFilterChange(filterText);
263
+ if (!isFirstRender.current) {
264
+ onFilterChange?.(textValue);
275
265
  }
276
- }, [onFilterChange, filterText]);
266
+ }, [onFilterChange, textValue]);
277
267
  (0, _react.useEffect)(() => {
278
268
  isFirstRender.current = false;
279
269
  }, []);
280
270
  function handleTextboxClick(event) {
281
271
  isMouseDownReported.current = false;
282
- if (onClick) {
283
- onClick(event);
272
+ onClick?.(event);
273
+ if (openOnFocus && isOpenedByFocus) {
274
+ setIsOpenedByFocus(false);
275
+ return;
284
276
  }
285
- if (!openOnFocus || openOnFocus && !isOpenedByFocus.current) {
286
- if (isOpen) {
287
- setFilterText("");
288
- setOpenState(false);
289
- return;
290
- }
291
- onOpen?.();
292
- setOpenState(true);
277
+ if (open) {
278
+ setTextValue("");
279
+ setOpen(false);
293
280
  } else {
294
- isOpenedByFocus.current = false;
295
- }
296
- }
297
- function handleDropdownIconClick(event) {
298
- isMouseDownReported.current = false;
299
- if (onClick) {
300
- onClick(event);
281
+ onOpen?.();
282
+ setOpen(true);
301
283
  }
302
- setOpenState(isAlreadyOpen => {
303
- if (isAlreadyOpen) {
304
- setFilterText("");
305
- return false;
306
- }
307
- if (onOpen) {
308
- onOpen();
309
- }
310
- return true;
311
- });
312
284
  }
313
285
  function handleTextboxBlur(event) {
314
286
  isMouseDownOnInput.current = false;
315
287
  if (isMouseDownReported.current) {
316
288
  return;
317
289
  }
318
- isInputFocused.current = false;
319
- if (onBlur) {
320
- onBlur(event);
321
- }
290
+ onBlur?.(event);
291
+ setTextValue("");
292
+ setOpen(false);
322
293
  }
323
- function handleTextboxMouseDown(event) {
294
+ function handleTextboxMouseDown() {
324
295
  isMouseDownReported.current = true;
325
- if (event.target.dataset.element === "input") {
326
- isMouseDownOnInput.current = true;
327
- }
296
+ isMouseDownOnInput.current = true;
328
297
  }
329
298
  function handleListMouseDown() {
330
299
  isMouseDownReported.current = true;
331
300
  }
332
301
  function handleTextboxFocus(event) {
333
- const triggerFocus = () => onFocus?.(event);
302
+ onFocus?.(event);
334
303
  if (openOnFocus) {
335
- if (focusTimer.current) {
336
- clearTimeout(focusTimer.current);
304
+ if (open) return;
305
+ onOpen?.();
306
+ if (isMouseDownOnInput.current) {
307
+ setIsOpenedByFocus(true);
337
308
  }
338
-
339
- // we need to use a timeout here as there is a race condition when rendered in a modal
340
- // whereby the select list isn't visible when the select is auto focused straight away
341
- focusTimer.current = setTimeout(() => {
342
- setOpenState(isAlreadyOpen => {
343
- if (isAlreadyOpen) {
344
- return true;
345
- }
346
- if (onOpen) {
347
- onOpen();
348
- }
349
- if (onFocus && !isInputFocused.current) {
350
- triggerFocus();
351
- isInputFocused.current = true;
352
- }
353
- if (isMouseDownReported.current && !isMouseDownOnInput.current) {
354
- isOpenedByFocus.current = false;
355
- return false;
356
- }
357
- if (isMouseDownOnInput.current) {
358
- isOpenedByFocus.current = true;
359
- }
360
- return true;
361
- });
362
- });
363
- } else if (onFocus && !isInputFocused.current) {
364
- triggerFocus();
365
- isInputFocused.current = true;
309
+ setOpen(true);
366
310
  }
367
311
  }
368
312
  const onSelectOption = (0, _react.useCallback)(optionData => {
@@ -392,8 +336,8 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
392
336
  }, selectionConfirmed);
393
337
  }, [textboxRef, actualValue, updateValue]);
394
338
  const onSelectListClose = (0, _react.useCallback)(() => {
395
- setOpenState(false);
396
- setFilterText("");
339
+ setOpen(false);
340
+ setTextValue("");
397
341
  }, []);
398
342
  const assignInput = (0, _react.useCallback)(element => {
399
343
  if (!element) return;
@@ -420,8 +364,6 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
420
364
  onMouseDown: handleTextboxMouseDown,
421
365
  onFocus: handleTextboxFocus,
422
366
  onBlur: handleTextboxBlur,
423
- iconOnClick: handleDropdownIconClick,
424
- iconOnMouseDown: handleTextboxMouseDown,
425
367
  onKeyDown: handleTextboxKeydown,
426
368
  onChange: handleTextboxChange,
427
369
  tooltipPosition,
@@ -450,7 +392,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
450
392
  onSelect: onSelectOption,
451
393
  onSelectListClose: onSelectListClose,
452
394
  onMouseDown: handleListMouseDown,
453
- filterText: filterText.trim(),
395
+ filterText: textValue.trim(),
454
396
  highlightedValue: highlightedValue,
455
397
  noResultsMessage: noResultsMessage,
456
398
  isLoading: isLoading,
@@ -460,7 +402,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
460
402
  listMaxHeight: listMaxHeight,
461
403
  flipEnabled: flipEnabled,
462
404
  multiselectValues: actualValue,
463
- isOpen: isOpen,
405
+ isOpen: open,
464
406
  enableVirtualScroll: enableVirtualScroll,
465
407
  virtualScrollOverscan: virtualScrollOverscan,
466
408
  listWidth: listWidth
@@ -474,7 +416,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
474
416
  "data-component": dataComponent,
475
417
  "data-role": dataRole,
476
418
  "data-element": dataElement,
477
- isOpen: isOpen
419
+ isOpen: open
478
420
  }, marginProps), /*#__PURE__*/_react.default.createElement("div", {
479
421
  ref: containerRef
480
422
  }, /*#__PURE__*/_react.default.createElement(_multiSelect.StyledAccessibilityLabelContainer, {
@@ -488,7 +430,7 @@ const MultiSelect = exports.MultiSelect = /*#__PURE__*/_react.default.forwardRef
488
430
  ariaLabel: ariaLabel,
489
431
  ariaLabelledby: ariaLabelledby,
490
432
  hasTextCursor: true,
491
- isOpen: isOpen,
433
+ isOpen: open,
492
434
  labelId: labelId
493
435
  }, getTextboxProps()))), selectList);
494
436
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "144.2.2",
3
+ "version": "144.3.0",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",