carbon-react 114.13.1 → 114.13.2

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 (32) hide show
  1. package/esm/__internal__/input-icon-toggle/input-icon-toggle.component.js +2 -1
  2. package/esm/components/link/link.component.d.ts +2 -0
  3. package/esm/components/link/link.component.js +7 -1
  4. package/esm/components/menu/__internal__/keyboard-navigation/index.d.ts +4 -2
  5. package/esm/components/menu/__internal__/keyboard-navigation/index.js +16 -15
  6. package/esm/components/menu/__internal__/locators.d.ts +6 -0
  7. package/esm/components/menu/__internal__/locators.js +6 -0
  8. package/esm/components/menu/__internal__/submenu/submenu.component.js +109 -108
  9. package/esm/components/menu/menu-full-screen/menu-full-screen.component.js +1 -2
  10. package/esm/components/menu/menu-item/index.js +0 -1
  11. package/esm/components/menu/menu-item/menu-item.component.js +77 -51
  12. package/esm/components/menu/menu-item/menu-item.d.ts +7 -3
  13. package/esm/components/menu/menu.component.js +33 -37
  14. package/esm/components/menu/menu.context.d.ts +2 -2
  15. package/esm/components/menu/menu.context.js +2 -2
  16. package/esm/components/menu/scrollable-block/scrollable-block.component.js +6 -24
  17. package/lib/__internal__/input-icon-toggle/input-icon-toggle.component.js +2 -1
  18. package/lib/components/link/link.component.d.ts +2 -0
  19. package/lib/components/link/link.component.js +7 -1
  20. package/lib/components/menu/__internal__/keyboard-navigation/index.d.ts +4 -2
  21. package/lib/components/menu/__internal__/keyboard-navigation/index.js +16 -15
  22. package/lib/components/menu/__internal__/locators.d.ts +6 -0
  23. package/lib/components/menu/__internal__/locators.js +18 -0
  24. package/lib/components/menu/__internal__/submenu/submenu.component.js +111 -113
  25. package/lib/components/menu/menu-full-screen/menu-full-screen.component.js +1 -2
  26. package/lib/components/menu/menu-item/menu-item.component.js +76 -52
  27. package/lib/components/menu/menu-item/menu-item.d.ts +7 -3
  28. package/lib/components/menu/menu.component.js +33 -37
  29. package/lib/components/menu/menu.context.d.ts +2 -2
  30. package/lib/components/menu/menu.context.js +2 -2
  31. package/lib/components/menu/scrollable-block/scrollable-block.component.js +6 -25
  32. package/package.json +1 -1
@@ -19,16 +19,16 @@ var _events = _interopRequireDefault(require("../../../../__internal__/utils/hel
19
19
 
20
20
  var _menu = _interopRequireDefault(require("../../menu.context"));
21
21
 
22
- var _menuItem2 = _interopRequireDefault(require("../../menu-item"));
23
-
24
22
  var _keyboardNavigation = require("../keyboard-navigation");
25
23
 
26
- var _scrollableBlock = _interopRequireDefault(require("../../scrollable-block"));
27
-
28
24
  var _submenu2 = _interopRequireDefault(require("./submenu.context"));
29
25
 
30
26
  var _useClickAwayListener = _interopRequireDefault(require("../../../../hooks/__internal__/useClickAwayListener"));
31
27
 
28
+ var _guid = _interopRequireDefault(require("../../../../__internal__/utils/helpers/guid"));
29
+
30
+ var _locators = require("../locators");
31
+
32
32
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
33
 
34
34
  function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
@@ -53,50 +53,44 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
53
53
  onSubmenuOpen,
54
54
  onSubmenuClose,
55
55
  onClick,
56
- indexInMenu,
57
56
  ...rest
58
57
  }, ref) => {
59
- const [blockDoubleFocus, setBlockDoubleFocus] = (0, _react.useState)(false);
58
+ const submenuRef = (0, _react.useRef)(null);
59
+ const submenuId = (0, _react.useRef)((0, _guid.default)());
60
60
  const menuContext = (0, _react.useContext)(_menu.default);
61
61
  const {
62
62
  inFullscreenView,
63
- openSubmenuIndex,
64
- setOpenSubmenuIndex
63
+ openSubmenuId,
64
+ setOpenSubmenuId
65
65
  } = menuContext;
66
66
  const [submenuOpen, setSubmenuOpen] = (0, _react.useState)(false);
67
- const [submenuFocusIndex, setSubmenuFocusIndex] = (0, _react.useState)(undefined);
67
+ const [submenuFocusId, setSubmenuFocusId] = (0, _react.useState)(null);
68
+ const [submenuItemIds, setSubmenuItemIds] = (0, _react.useState)([]);
68
69
  const [characterString, setCharacterString] = (0, _react.useState)("");
69
-
70
- const formattedChildren = _react.default.Children.map(children, child => {
71
- if (child.type === _scrollableBlock.default) {
72
- const blockChildren = [...child.props.children];
73
-
74
- if (child.props.parent) {
75
- blockChildren.unshift( /*#__PURE__*/_react.default.createElement(_menuItem2.default, null, child.props.parent));
76
- }
77
-
78
- return blockChildren;
79
- }
80
-
81
- return child;
82
- });
83
-
84
- const arrayOfFormattedChildren = _react.default.Children.toArray(formattedChildren);
85
-
86
- const numberOfChildren = (0, _react.useMemo)(() => _react.default.Children.count(formattedChildren), [formattedChildren]);
70
+ const shiftTabPressed = (0, _react.useRef)(false);
71
+ const registerItem = (0, _react.useCallback)(id => {
72
+ setSubmenuItemIds(prevState => {
73
+ return [...prevState, id];
74
+ });
75
+ }, []);
76
+ const unregisterItem = (0, _react.useCallback)(id => {
77
+ setSubmenuItemIds(prevState => {
78
+ return prevState.filter(itemId => itemId !== id);
79
+ });
80
+ }, []);
81
+ const numberOfChildren = submenuItemIds.length;
87
82
  const blockIndex = (0, _react.useMemo)(() => {
88
- var _childrenArray$index;
89
-
90
- const childrenArray = _react.default.Children.toArray(children);
83
+ if (submenuOpen && numberOfChildren) {
84
+ var _submenuRef$current, _submenuRef$current2;
91
85
 
92
- let index = childrenArray.findIndex(item => item.type === _scrollableBlock.default);
93
-
94
- if ((_childrenArray$index = childrenArray[index]) !== null && _childrenArray$index !== void 0 && _childrenArray$index.props.parent) {
95
- index += 1;
86
+ const childrenArray = Array.from((_submenuRef$current = submenuRef.current) === null || _submenuRef$current === void 0 ? void 0 : _submenuRef$current.querySelectorAll(_locators.BLOCK_INDEX_SELECTOR));
87
+ const scrollableBlock = (_submenuRef$current2 = submenuRef.current) === null || _submenuRef$current2 === void 0 ? void 0 : _submenuRef$current2.querySelector(`[data-component='${_locators.SCROLLABLE_BLOCK}']`);
88
+ const index = childrenArray.indexOf(scrollableBlock);
89
+ return scrollableBlock !== null && scrollableBlock !== void 0 && scrollableBlock.querySelector(`[data-component='${_locators.SCROLLABLE_BLOCK_PARENT}']`) ? index + 1 : index;
96
90
  }
97
91
 
98
- return index;
99
- }, [children]);
92
+ return -1;
93
+ }, [submenuOpen, numberOfChildren]);
100
94
  const characterTimer = (0, _react.useRef)();
101
95
  const startCharacterTimeout = (0, _react.useCallback)(() => {
102
96
  characterTimer.current = setTimeout(() => {
@@ -109,83 +103,88 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
109
103
  }, [startCharacterTimeout]);
110
104
  const openSubmenu = (0, _react.useCallback)(() => {
111
105
  setSubmenuOpen(true);
112
- setOpenSubmenuIndex(indexInMenu);
113
- if (onSubmenuOpen) onSubmenuOpen();
114
- }, [onSubmenuOpen, indexInMenu, setOpenSubmenuIndex]);
106
+ setOpenSubmenuId(submenuId.current);
107
+
108
+ if (onSubmenuOpen) {
109
+ onSubmenuOpen();
110
+ }
111
+ }, [onSubmenuOpen, setOpenSubmenuId]);
115
112
  const closeSubmenu = (0, _react.useCallback)(() => {
113
+ shiftTabPressed.current = false;
116
114
  setSubmenuOpen(false);
115
+ setSubmenuFocusId(null);
117
116
 
118
- if (openSubmenuIndex === indexInMenu) {
119
- setOpenSubmenuIndex(null);
117
+ if (onSubmenuClose) {
118
+ onSubmenuClose();
120
119
  }
121
120
 
122
- if (onSubmenuClose) onSubmenuClose();
123
- setSubmenuFocusIndex(undefined);
124
- setBlockDoubleFocus(false);
125
121
  setCharacterString("");
126
- }, [onSubmenuClose, setOpenSubmenuIndex, indexInMenu, openSubmenuIndex]);
122
+ }, [onSubmenuClose]);
127
123
  (0, _react.useEffect)(() => {
128
- if (openSubmenuIndex !== indexInMenu) {
124
+ if (openSubmenuId && openSubmenuId !== submenuId.current) {
129
125
  closeSubmenu();
130
126
  }
131
- }, [openSubmenuIndex, indexInMenu, closeSubmenu]);
132
- const handleKeyDown = (0, _react.useCallback)((event, index = submenuFocusIndex) => {
127
+ }, [openSubmenuId, closeSubmenu]);
128
+ const findCurrentIndex = (0, _react.useCallback)(id => {
129
+ const index = submenuItemIds.findIndex(itemId => itemId === id);
130
+ return index === -1 ? 0 : index;
131
+ }, [submenuItemIds]);
132
+ const handleKeyDown = (0, _react.useCallback)(event => {
133
133
  if (!submenuOpen) {
134
134
  if (_events.default.isEnterKey(event) || _events.default.isSpaceKey(event) || _events.default.isDownKey(event) || _events.default.isUpKey(event)) {
135
135
  event.preventDefault();
136
136
  openSubmenu();
137
-
138
- if (!href) {
139
- setSubmenuFocusIndex(0);
140
- }
141
- }
142
-
143
- if (!event.defaultPrevented) {
144
- menuContext.handleKeyDown(event);
145
137
  }
146
138
  }
147
139
 
148
140
  if (submenuOpen) {
141
+ const index = findCurrentIndex(submenuFocusId);
149
142
  let nextIndex = index;
150
143
 
144
+ if (href && !submenuFocusId) {
145
+ if (_events.default.isDownKey(event) || _events.default.isUpKey(event) || _events.default.isTabKey(event) && !_events.default.isShiftKey(event)) {
146
+ event.preventDefault();
147
+ setSubmenuFocusId(submenuItemIds[0]);
148
+ return;
149
+ }
150
+ }
151
+
151
152
  if (_events.default.isTabKey(event) && !_events.default.isShiftKey(event)) {
152
- if (index === numberOfChildren - 1) {
153
+ if (nextIndex === numberOfChildren - 1) {
153
154
  closeSubmenu();
154
155
  return;
155
156
  }
156
157
 
157
- nextIndex = index + 1;
158
- setBlockDoubleFocus(true);
158
+ shiftTabPressed.current = false;
159
+ nextIndex += 1;
159
160
  }
160
161
 
161
162
  if (_events.default.isTabKey(event) && _events.default.isShiftKey(event)) {
162
- if (index === 0) {
163
+ if (nextIndex === 0) {
163
164
  closeSubmenu();
164
165
  return;
165
166
  }
166
167
 
167
- nextIndex = index - 1;
168
- setBlockDoubleFocus(true);
168
+ shiftTabPressed.current = true;
169
+ nextIndex -= 1;
169
170
  }
170
171
 
171
172
  if (_events.default.isDownKey(event)) {
172
173
  event.preventDefault();
174
+ shiftTabPressed.current = false;
173
175
 
174
- if (index < numberOfChildren - 1) {
175
- nextIndex = index + 1;
176
+ if (nextIndex < numberOfChildren - 1) {
177
+ nextIndex += 1;
176
178
  }
177
-
178
- setBlockDoubleFocus(false);
179
179
  }
180
180
 
181
181
  if (_events.default.isUpKey(event)) {
182
182
  event.preventDefault();
183
+ shiftTabPressed.current = false;
183
184
 
184
- if (index > 0) {
185
- nextIndex = index - 1;
185
+ if (nextIndex > 0) {
186
+ nextIndex -= 1;
186
187
  }
187
-
188
- setBlockDoubleFocus(false);
189
188
  }
190
189
 
191
190
  if (_events.default.isEscKey(event)) {
@@ -196,16 +195,19 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
196
195
 
197
196
  if (_events.default.isHomeKey(event)) {
198
197
  event.preventDefault();
198
+ shiftTabPressed.current = false;
199
199
  nextIndex = 0;
200
200
  }
201
201
 
202
202
  if (_events.default.isEndKey(event)) {
203
203
  event.preventDefault();
204
+ shiftTabPressed.current = false;
204
205
  nextIndex = numberOfChildren - 1;
205
206
  }
206
207
 
207
208
  if (event.key.length === 1) {
208
209
  event.stopPropagation();
210
+ shiftTabPressed.current = false;
209
211
 
210
212
  if (characterTimer.current) {
211
213
  restartCharacterTimeout();
@@ -224,31 +226,21 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
224
226
  setTimeout(() => closeSubmenu());
225
227
  }
226
228
 
227
- if (href && index === undefined) {
228
- if (_events.default.isEnterKey(event) || _events.default.isTabKey(event) && _events.default.isShiftKey(event)) {
229
- closeSubmenu();
230
- return;
231
- }
232
-
233
- if (_events.default.isSpaceKey(event) || _events.default.isDownKey(event) || _events.default.isUpKey(event) || _events.default.isTabKey(event)) {
234
- nextIndex = 0;
235
- }
236
- } // Defensive check in case an unhandled key event from a child component
237
- // has bubbled up
238
-
239
-
240
- if (!nextIndex && nextIndex !== 0) return; // Check that next index contains a MenuItem
241
- // If not, call handleKeyDown again
242
-
243
- const nextChild = arrayOfFormattedChildren[nextIndex];
229
+ if (href && _events.default.isEnterKey(event)) {
230
+ closeSubmenu();
231
+ return;
232
+ }
244
233
 
245
- if ((nextChild === null || nextChild === void 0 ? void 0 : nextChild.type) === _menuItem2.default) {
246
- setSubmenuFocusIndex(nextIndex);
247
- } else {
248
- handleKeyDown(event, nextIndex);
234
+ if (nextIndex !== index) {
235
+ setSubmenuFocusId(submenuItemIds[nextIndex]);
249
236
  }
250
237
  }
251
- }, [submenuFocusIndex, submenuOpen, href, menuContext, arrayOfFormattedChildren, numberOfChildren, openSubmenu, closeSubmenu, onKeyDown, characterString, restartCharacterTimeout, startCharacterTimeout]);
238
+ }, [submenuItemIds, submenuOpen, href, numberOfChildren, submenuFocusId, findCurrentIndex, openSubmenu, closeSubmenu, onKeyDown, characterString, restartCharacterTimeout, startCharacterTimeout]);
239
+ (0, _react.useEffect)(() => {
240
+ if (submenuOpen && !href && !submenuFocusId && submenuItemIds.length) {
241
+ setSubmenuFocusId(submenuItemIds[0]);
242
+ }
243
+ }, [submenuOpen, href, submenuFocusId, submenuItemIds]);
252
244
 
253
245
  const handleClickAway = () => {
254
246
  document.removeEventListener("click", handleClickAway);
@@ -265,11 +257,16 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
265
257
 
266
258
  (0, _react.useEffect)(() => {
267
259
  if (characterString !== "") {
268
- const nextIndex = (0, _keyboardNavigation.characterNavigation)(characterString, _react.default.Children.toArray(formattedChildren), submenuFocusIndex);
269
- setSubmenuFocusIndex(nextIndex);
270
- } // eslint-disable-next-line react-hooks/exhaustive-deps
260
+ var _submenuRef$current3;
271
261
 
272
- }, [characterString]);
262
+ const submenuChildren = Array.from((_submenuRef$current3 = submenuRef.current) === null || _submenuRef$current3 === void 0 ? void 0 : _submenuRef$current3.querySelectorAll(_locators.ALL_CHILDREN_SELECTOR));
263
+ const nextItem = (0, _keyboardNavigation.characterNavigation)(characterString, submenuChildren);
264
+
265
+ if (nextItem) {
266
+ setSubmenuFocusId(nextItem.id);
267
+ }
268
+ }
269
+ }, [characterString, submenuItemIds]);
273
270
  const handleClickInside = (0, _useClickAwayListener.default)(handleClickAway);
274
271
 
275
272
  if (inFullscreenView) {
@@ -293,15 +290,17 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
293
290
  "data-component": "submenu",
294
291
  variant: variant,
295
292
  menuType: menuContext.menuType,
296
- inFullscreenView: inFullscreenView
297
- }, _react.default.Children.map(children, (child, index) => /*#__PURE__*/_react.default.createElement(_submenu2.default.Provider, {
293
+ inFullscreenView: inFullscreenView,
294
+ ref: submenuRef
295
+ }, /*#__PURE__*/_react.default.createElement(_submenu2.default.Provider, {
298
296
  value: {
299
- isFocused: submenuFocusIndex === index,
300
- focusIndex: submenuFocusIndex,
301
297
  handleKeyDown,
302
- blockIndex
298
+ blockIndex,
299
+ registerItem,
300
+ unregisterItem,
301
+ updateFocusId: setSubmenuFocusId
303
302
  }
304
- }, child))));
303
+ }, children)));
305
304
  }
306
305
 
307
306
  return /*#__PURE__*/_react.default.createElement(_submenu.StyledSubmenuWrapper, {
@@ -309,7 +308,8 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
309
308
  onMouseOver: !clickToOpen ? () => openSubmenu() : undefined,
310
309
  onMouseLeave: () => closeSubmenu(),
311
310
  isSubmenuOpen: submenuOpen,
312
- onClick: handleClickInside
311
+ onClick: handleClickInside,
312
+ ref: submenuRef
313
313
  }, /*#__PURE__*/_react.default.createElement(_menuItem.default, _extends({}, rest, {
314
314
  className: className,
315
315
  menuType: menuContext.menuType,
@@ -333,16 +333,17 @@ const Submenu = /*#__PURE__*/_react.default.forwardRef(({
333
333
  variant: variant,
334
334
  menuType: menuContext.menuType,
335
335
  role: blockIndex === 0 ? "presentation" : "list"
336
- }, _react.default.Children.map(children, (child, index) => /*#__PURE__*/_react.default.createElement(_submenu2.default.Provider, {
336
+ }, /*#__PURE__*/_react.default.createElement(_submenu2.default.Provider, {
337
337
  value: {
338
- isFocused: !blockDoubleFocus && submenuFocusIndex === index,
339
- focusIndex: submenuFocusIndex,
338
+ submenuFocusId,
340
339
  handleKeyDown,
341
340
  blockIndex,
342
- updateFocusIndex: setSubmenuFocusIndex,
343
- itemIndex: child.type === _menuItem2.default ? index : undefined
341
+ registerItem,
342
+ unregisterItem,
343
+ updateFocusId: setSubmenuFocusId,
344
+ shiftTabPressed: shiftTabPressed.current
344
345
  }
345
- }, child))));
346
+ }, children)));
346
347
  });
347
348
 
348
349
  Submenu.propTypes = {
@@ -393,10 +394,7 @@ Submenu.propTypes = {
393
394
  onSubmenuClose: _propTypes.default.func,
394
395
 
395
396
  /** Callback triggered when the top-level menu item is clicked */
396
- onClick: _propTypes.default.func,
397
-
398
- /** index of child in the parent menu */
399
- indexInMenu: _propTypes.default.number
397
+ onClick: _propTypes.default.func
400
398
  };
401
399
  var _default = Submenu;
402
400
  exports.default = _default;
@@ -109,8 +109,7 @@ const MenuFullscreen = ({
109
109
  value: {
110
110
  inFullscreenView: true,
111
111
  menuType,
112
- inMenu: true,
113
- setOpenSubmenuIndex: () => {}
112
+ inMenu: true
114
113
  }
115
114
  }, _react.default.Children.map(children, (child, index) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, child, index < _react.default.Children.count(children) - 1 && /*#__PURE__*/_react.default.createElement(_menuDivider.default, null))))))))));
116
115
  };
@@ -27,7 +27,7 @@ var _submenu2 = _interopRequireDefault(require("../__internal__/submenu/submenu.
27
27
 
28
28
  var _menu2 = require("../menu.style");
29
29
 
30
- var _search = _interopRequireDefault(require("../../search"));
30
+ var _guid = _interopRequireDefault(require("../../../__internal__/utils/helpers/guid"));
31
31
 
32
32
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
33
 
@@ -57,43 +57,70 @@ const MenuItem = ({
57
57
  onSubmenuClose,
58
58
  overrideColor,
59
59
  rel,
60
- isFocused,
61
60
  ...rest
62
61
  }) => {
63
- const menuContext = (0, _react.useContext)(_menu.default);
62
+ var _ref$current, _ref$current2;
63
+
64
+ const {
65
+ inFullscreenView,
66
+ registerItem,
67
+ unregisterItem,
68
+ focusId,
69
+ menuType,
70
+ openSubmenuId
71
+ } = (0, _react.useContext)(_menu.default);
72
+ const menuItemId = (0, _react.useRef)((0, _guid.default)());
64
73
  const submenuContext = (0, _react.useContext)(_submenu2.default);
65
- const ref = (0, _react.useRef)(null);
66
- const focusFromMenu = isFocused;
67
- const focusFromSubmenu = submenuContext.isFocused;
68
- const isChildSearch = (0, _react.useRef)(false);
69
- const childRef = (0, _react.useRef)();
70
74
  const {
71
- inFullscreenView
72
- } = menuContext;
75
+ registerItem: registerSubmenuItem,
76
+ unregisterItem: unregisterSubmenuItem,
77
+ submenuFocusId,
78
+ updateFocusId: updateSubmenuFocusId,
79
+ handleKeyDown: handleSubmenuKeyDown,
80
+ shiftTabPressed
81
+ } = submenuContext;
82
+ const ref = (0, _react.useRef)(null);
83
+ const focusFromMenu = focusId === menuItemId.current;
84
+ const focusFromSubmenu = submenuFocusId ? submenuFocusId === menuItemId.current : undefined;
85
+ const inputRef = (0, _react.useRef)(null);
86
+ const inputIcon = (0, _react.useRef)(null);
87
+ inputIcon.current = (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector("[data-element='input-icon-toggle']");
88
+ inputRef.current = (_ref$current2 = ref.current) === null || _ref$current2 === void 0 ? void 0 : _ref$current2.querySelector("[data-element='input']");
89
+ const focusRef = inputRef.current ? inputRef : ref;
90
+ (0, _react.useEffect)(() => {
91
+ const id = menuItemId.current;
73
92
 
74
- const childrenItems = _react.default.Children.map(children, child => {
75
- if ((child === null || child === void 0 ? void 0 : child.type) === _search.default) {
76
- isChildSearch.current = true;
93
+ if (registerSubmenuItem) {
94
+ registerSubmenuItem(id);
95
+ } else if (registerItem) {
96
+ registerItem(id);
77
97
  }
78
98
 
79
- return child;
80
- });
81
-
82
- const focusRef = isChildSearch.current ? childRef : ref;
99
+ return () => {
100
+ if (unregisterSubmenuItem) {
101
+ unregisterSubmenuItem(id);
102
+ } else if (unregisterItem) {
103
+ unregisterItem(id);
104
+ }
105
+ };
106
+ }, [registerSubmenuItem, registerItem, unregisterSubmenuItem, unregisterItem]);
83
107
  (0, _react.useEffect)(() => {
84
- if (focusFromSubmenu === undefined && focusFromMenu) {
108
+ var _inputIcon$current;
109
+
110
+ if (!openSubmenuId && focusFromSubmenu === undefined && focusFromMenu) {
85
111
  focusRef.current.focus();
86
- } else if (focusFromSubmenu) {
112
+ } else if (focusFromSubmenu && !(shiftTabPressed && ((_inputIcon$current = inputIcon.current) === null || _inputIcon$current === void 0 ? void 0 : _inputIcon$current.getAttribute("tabindex")) === "0")) {
87
113
  focusRef.current.focus();
88
114
  }
89
- }, [focusFromMenu, focusFromSubmenu, focusRef]);
115
+ }, [openSubmenuId, focusFromMenu, focusFromSubmenu, inputIcon, shiftTabPressed, focusRef]);
90
116
  const updateFocusOnClick = (0, _react.useCallback)(() => {
91
- /* istanbul ignore else */
92
- if (submenuContext.updateFocusIndex) {
93
- submenuContext.updateFocusIndex(submenuContext.itemIndex);
117
+ if (updateSubmenuFocusId) {
118
+ updateSubmenuFocusId(menuItemId.current);
94
119
  }
95
- }, [submenuContext]);
120
+ }, [updateSubmenuFocusId]);
96
121
  const handleKeyDown = (0, _react.useCallback)(event => {
122
+ var _inputIcon$current2, _inputRef$current;
123
+
97
124
  if (onKeyDown) {
98
125
  onKeyDown(event);
99
126
  }
@@ -102,16 +129,16 @@ const MenuItem = ({
102
129
  ref.current.focus();
103
130
  }
104
131
 
105
- if (submenuContext.handleKeyDown !== undefined) {
106
- var _focusRef$current;
132
+ const shouldFocusIcon = ((_inputIcon$current2 = inputIcon.current) === null || _inputIcon$current2 === void 0 ? void 0 : _inputIcon$current2.getAttribute("tabindex")) === "0" && document.activeElement === inputRef.current && ((_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.value); // let natural tab order move focus if input icon is tabbable
107
133
 
108
- if (!(isChildSearch.current && document.activeElement === focusRef.current && (_focusRef$current = focusRef.current) !== null && _focusRef$current !== void 0 && _focusRef$current.value)) {
109
- submenuContext.handleKeyDown(event);
110
- }
111
- } else {
112
- menuContext.handleKeyDown(event);
134
+ if (_events.default.isTabKey(event) && (!_events.default.isShiftKey(event) && shouldFocusIcon || _events.default.isShiftKey(event) && document.activeElement === inputIcon.current)) {
135
+ return;
113
136
  }
114
- }, [focusRef, menuContext, onKeyDown, submenuContext]);
137
+
138
+ if (handleSubmenuKeyDown) {
139
+ handleSubmenuKeyDown(event);
140
+ }
141
+ }, [onKeyDown, handleSubmenuKeyDown, inputIcon]);
115
142
  const classes = (0, _react.useMemo)(() => (0, _classnames.default)({
116
143
  "carbon-menu-item--has-link": href || onClick
117
144
  }), [href, onClick]);
@@ -120,7 +147,7 @@ const MenuItem = ({
120
147
  href,
121
148
  target,
122
149
  rel,
123
- onClick: onClick || (isChildSearch.current ? updateFocusOnClick : undefined),
150
+ onClick: onClick || (inputRef.current ? updateFocusOnClick : undefined),
124
151
  icon,
125
152
  selected,
126
153
  variant,
@@ -128,11 +155,8 @@ const MenuItem = ({
128
155
  overrideColor,
129
156
  ref
130
157
  };
131
- const clonedChildren = isChildSearch.current ? childrenItems.map(child => /*#__PURE__*/_react.default.cloneElement(child, {
132
- ref: childRef
133
- })) : children;
134
158
 
135
- const getTitle = title => maxWidth && typeof title === "string" ? title : "";
159
+ const getTitle = title => maxWidth && typeof title === "string" ? title : undefined;
136
160
 
137
161
  const itemMaxWidth = !inFullscreenView ? maxWidth : undefined;
138
162
  const asPassiveItem = !(onClick || href);
@@ -140,13 +164,14 @@ const MenuItem = ({
140
164
  if (submenu) {
141
165
  return /*#__PURE__*/_react.default.createElement(_menu2.StyledMenuItem, _extends({
142
166
  "data-component": "menu-item",
143
- menuType: menuContext.menuType,
167
+ menuType: menuType,
144
168
  display: "inline-block",
145
169
  title: getTitle(submenu),
146
170
  maxWidth: itemMaxWidth,
147
171
  onClick: updateFocusOnClick
148
172
  }, rest, {
149
- inFullscreenView: inFullscreenView
173
+ inFullscreenView: inFullscreenView,
174
+ id: menuItemId.current
150
175
  }), /*#__PURE__*/_react.default.createElement(_submenu.default, _extends({}, typeof submenu !== "boolean" && {
151
176
  title: submenu
152
177
  }, {
@@ -158,29 +183,31 @@ const MenuItem = ({
158
183
  ariaLabel: ariaLabel,
159
184
  onSubmenuOpen: onSubmenuOpen,
160
185
  onSubmenuClose: onSubmenuClose
161
- }, elementProps, rest), childrenItems));
186
+ }, elementProps, rest), children));
162
187
  }
163
188
 
164
189
  return /*#__PURE__*/_react.default.createElement(_menu2.StyledMenuItem, _extends({
165
190
  "data-component": "menu-item",
166
- menuType: menuContext.menuType,
167
- inSubmenu: submenuContext.handleKeyDown !== undefined,
191
+ menuType: menuType,
192
+ inSubmenu: !!handleSubmenuKeyDown,
168
193
  display: "inline-block",
169
194
  title: getTitle(children),
170
195
  maxWidth: itemMaxWidth
171
196
  }, rest, {
172
197
  inFullscreenView: inFullscreenView && !Object.keys(submenuContext).length,
173
- menuOpen: menuOpen
198
+ menuOpen: menuOpen,
199
+ id: menuItemId.current
174
200
  }), /*#__PURE__*/_react.default.createElement(_menuItem.default, _extends({
175
- as: isChildSearch.current ? "div" : _link.default,
176
- isSearch: isChildSearch.current,
177
- menuType: menuContext.menuType
201
+ as: inputRef.current ? "div" : _link.default,
202
+ isSearch: inputRef.current,
203
+ menuType: menuType
178
204
  }, elementProps, {
179
205
  ariaLabel: ariaLabel,
180
206
  maxWidth: maxWidth,
181
207
  inFullscreenView: inFullscreenView,
182
- asPassiveItem: asPassiveItem
183
- }), clonedChildren));
208
+ asPassiveItem: asPassiveItem,
209
+ placeholderTabIndex: asPassiveItem
210
+ }), children));
184
211
  };
185
212
 
186
213
  MenuItem.propTypes = {
@@ -269,10 +296,7 @@ MenuItem.propTypes = {
269
296
  overrideColor: _propTypes.default.bool,
270
297
 
271
298
  /** @ignore @private */
272
- isFocused: _propTypes.default.bool,
273
-
274
- /** @ignore @private */
275
- indexInMenu: _propTypes.default.number
299
+ "data-component": _propTypes.default.string
276
300
  };
277
301
  MenuItem.displayName = "MenuItem";
278
302
  var _default = MenuItem;
@@ -31,10 +31,14 @@ export interface MenuItemBaseProps extends LayoutProps, FlexboxProps {
31
31
  onSubmenuOpen?: () => void;
32
32
  /** Callback triggered when submenu closes. Only valid with submenu prop */
33
33
  onSubmenuClose?: () => void;
34
- /** @ignore @private
35
- private prop, used inside ScrollableBlock to ensure the MenuItem's color variant overrides the CSS
36
- for other MenuItems inside the block */
34
+ /**
35
+ @ignore @private
36
+ private prop, used inside ScrollableBlock to ensure the MenuItem's color variant overrides the CSS
37
+ for other MenuItems inside the block
38
+ */
37
39
  overrideColor?: boolean;
40
+ /** @private @ignore */
41
+ "data-component"?: string;
38
42
  }
39
43
 
40
44
  export interface MenuWithChildren extends MenuItemBaseProps {