carbon-react 114.12.3 → 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 (38) hide show
  1. package/esm/__internal__/input-icon-toggle/input-icon-toggle.component.js +2 -1
  2. package/esm/components/anchor-navigation/anchor-navigation.component.js +30 -33
  3. package/esm/components/anchor-navigation/anchor-navigation.style.js +4 -0
  4. package/esm/components/date/__internal__/date-picker/date-picker.component.js +11 -7
  5. package/esm/components/link/link.component.d.ts +2 -0
  6. package/esm/components/link/link.component.js +7 -1
  7. package/esm/components/menu/__internal__/keyboard-navigation/index.d.ts +4 -2
  8. package/esm/components/menu/__internal__/keyboard-navigation/index.js +16 -15
  9. package/esm/components/menu/__internal__/locators.d.ts +6 -0
  10. package/esm/components/menu/__internal__/locators.js +6 -0
  11. package/esm/components/menu/__internal__/submenu/submenu.component.js +109 -108
  12. package/esm/components/menu/menu-full-screen/menu-full-screen.component.js +1 -2
  13. package/esm/components/menu/menu-item/index.js +0 -1
  14. package/esm/components/menu/menu-item/menu-item.component.js +77 -51
  15. package/esm/components/menu/menu-item/menu-item.d.ts +7 -3
  16. package/esm/components/menu/menu.component.js +33 -37
  17. package/esm/components/menu/menu.context.d.ts +2 -2
  18. package/esm/components/menu/menu.context.js +2 -2
  19. package/esm/components/menu/scrollable-block/scrollable-block.component.js +6 -24
  20. package/lib/__internal__/input-icon-toggle/input-icon-toggle.component.js +2 -1
  21. package/lib/components/anchor-navigation/anchor-navigation.component.js +30 -32
  22. package/lib/components/anchor-navigation/anchor-navigation.style.js +4 -0
  23. package/lib/components/date/__internal__/date-picker/date-picker.component.js +11 -7
  24. package/lib/components/link/link.component.d.ts +2 -0
  25. package/lib/components/link/link.component.js +7 -1
  26. package/lib/components/menu/__internal__/keyboard-navigation/index.d.ts +4 -2
  27. package/lib/components/menu/__internal__/keyboard-navigation/index.js +16 -15
  28. package/lib/components/menu/__internal__/locators.d.ts +6 -0
  29. package/lib/components/menu/__internal__/locators.js +18 -0
  30. package/lib/components/menu/__internal__/submenu/submenu.component.js +111 -113
  31. package/lib/components/menu/menu-full-screen/menu-full-screen.component.js +1 -2
  32. package/lib/components/menu/menu-item/menu-item.component.js +76 -52
  33. package/lib/components/menu/menu-item/menu-item.d.ts +7 -3
  34. package/lib/components/menu/menu.component.js +33 -37
  35. package/lib/components/menu/menu.context.d.ts +2 -2
  36. package/lib/components/menu/menu.context.js +2 -2
  37. package/lib/components/menu/scrollable-block/scrollable-block.component.js +6 -25
  38. package/package.json +1 -1
@@ -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 {
@@ -19,11 +19,11 @@ var _propTypes2 = _interopRequireDefault(require("@styled-system/prop-types"));
19
19
 
20
20
  var _menu = require("./menu.style");
21
21
 
22
- var _keyboardNavigation = require("./__internal__/keyboard-navigation");
22
+ var _menu2 = _interopRequireDefault(require("./menu.context"));
23
23
 
24
- var _events = _interopRequireDefault(require("../../__internal__/utils/helpers/events"));
24
+ var _keyboardNavigation = require("./__internal__/keyboard-navigation");
25
25
 
26
- var _menu2 = _interopRequireDefault(require("./menu.context"));
26
+ var _locators = require("./__internal__/locators");
27
27
 
28
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29
29
 
@@ -38,52 +38,48 @@ const Menu = ({
38
38
  children,
39
39
  ...rest
40
40
  }) => {
41
- const [focusedItemIndex, setFocusedItemIndex] = (0, _react.useState)(undefined);
42
- const [openSubmenuIndex, setOpenSubmenuIndex] = (0, _react.useState)(null);
41
+ const [openSubmenuId, setOpenSubmenuId] = (0, _react.useState)(null);
43
42
  const ref = (0, _react.useRef)();
44
- const handleKeyDown = (0, _react.useCallback)(event => {
45
- const newIndex = (0, _keyboardNavigation.menuKeyboardNavigation)(event, _react.default.Children.toArray(children));
46
- setFocusedItemIndex(newIndex);
47
- }, [children]);
48
- const onClickOutside = (0, _react.useCallback)(event => {
49
- // Reset the state of the menu when clicking elsewhere
50
- if (!_events.default.composedPath(event).includes(ref.current)) {
51
- setFocusedItemIndex(undefined);
52
- document.removeEventListener("click", onClickOutside);
53
- }
43
+ const [focusId, setFocusId] = (0, _react.useState)(undefined);
44
+ const [itemIds, setItemIds] = (0, _react.useState)([]);
45
+ const registerItem = (0, _react.useCallback)(id => {
46
+ setItemIds(prevState => {
47
+ return [...prevState, id];
48
+ });
49
+ }, []);
50
+ const unregisterItem = (0, _react.useCallback)(id => {
51
+ setItemIds(prevState => {
52
+ return prevState.filter(itemId => itemId !== id);
53
+ });
54
54
  }, []);
55
- (0, _react.useEffect)(() => {
56
- document.addEventListener("click", onClickOutside);
57
- return function cleanup() {
58
- document.removeEventListener("click", onClickOutside);
59
- };
60
- });
55
+
56
+ const handleKeyDown = event => {
57
+ /* istanbul ignore else */
58
+ if (ref.current) {
59
+ const focusableItems = Array.from(ref.current.querySelectorAll(_locators.MENU_ITEM_CHILDREN_LOCATOR));
60
+ const newIndex = (0, _keyboardNavigation.menuKeyboardNavigation)(event, focusableItems);
61
+ setFocusId(itemIds[newIndex]);
62
+ }
63
+ };
64
+
61
65
  return /*#__PURE__*/_react.default.createElement(_menu.StyledMenuWrapper, _extends({
62
66
  "data-component": "menu",
63
67
  menuType: menuType
64
68
  }, rest, {
65
69
  ref: ref,
66
- role: "list"
70
+ role: "list",
71
+ onKeyDown: handleKeyDown
67
72
  }), /*#__PURE__*/_react.default.createElement(_menu2.default.Provider, {
68
73
  value: {
69
74
  menuType,
70
- handleKeyDown,
71
75
  inMenu: true,
72
- openSubmenuIndex,
73
- setOpenSubmenuIndex
76
+ openSubmenuId,
77
+ setOpenSubmenuId,
78
+ focusId,
79
+ registerItem,
80
+ unregisterItem
74
81
  }
75
- }, _react.default.Children.map(children, (child, index) => {
76
- const isFocused = focusedItemIndex === index;
77
-
78
- if ( /*#__PURE__*/_react.default.isValidElement(child) && child.type.displayName === "MenuItem" && child.props.submenu) {
79
- return /*#__PURE__*/_react.default.cloneElement(child, {
80
- isFocused,
81
- indexInMenu: index
82
- });
83
- }
84
-
85
- return child;
86
- })));
82
+ }, children));
87
83
  };
88
84
 
89
85
  Menu.propTypes = {
@@ -1,8 +1,8 @@
1
1
  declare var _default: React.Context<{
2
2
  menuType: string;
3
3
  inMenu: boolean;
4
- openSubmenuIndex: null;
5
- setOpenSubmenuIndex: () => void;
4
+ openSubmenuId: null;
5
+ setOpenSubmenuId: () => void;
6
6
  }>;
7
7
  export default _default;
8
8
  import React from "react";
@@ -12,8 +12,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
12
12
  var _default = /*#__PURE__*/_react.default.createContext({
13
13
  menuType: "light",
14
14
  inMenu: false,
15
- openSubmenuIndex: null,
16
- setOpenSubmenuIndex:
15
+ openSubmenuId: null,
16
+ setOpenSubmenuId:
17
17
  /* istanbul ignore next */
18
18
  () => {}
19
19
  });
@@ -13,8 +13,6 @@ var _menu = _interopRequireDefault(require("../menu.context"));
13
13
 
14
14
  var _menuItem = _interopRequireDefault(require("../menu-item"));
15
15
 
16
- var _submenu = _interopRequireDefault(require("../__internal__/submenu/submenu.context"));
17
-
18
16
  var _scrollableBlock = _interopRequireDefault(require("./scrollable-block.style"));
19
17
 
20
18
  var _box = _interopRequireDefault(require("../../box"));
@@ -35,13 +33,9 @@ const ScrollableBlock = ({
35
33
  parentVariant,
36
34
  ...rest
37
35
  }) => {
38
- const menuContext = (0, _react.useContext)(_menu.default);
39
- const submenuContext = (0, _react.useContext)(_submenu.default);
40
36
  const {
41
- blockIndex,
42
- focusIndex,
43
- handleKeyDown
44
- } = submenuContext;
37
+ menuType
38
+ } = (0, _react.useContext)(_menu.default);
45
39
  const scrollVariants = {
46
40
  light: "light",
47
41
  dark: "dark",
@@ -50,35 +44,22 @@ const ScrollableBlock = ({
50
44
  };
51
45
  return /*#__PURE__*/_react.default.createElement(_scrollableBlock.default, _extends({
52
46
  "data-component": "submenu-scrollable-block",
53
- menuType: menuContext.menuType,
47
+ menuType: menuType,
54
48
  variant: variant
55
49
  }, rest), parent && /*#__PURE__*/_react.default.createElement(_menuItem.default, {
50
+ "data-component": "scrollable-block-parent",
56
51
  overrideColor: true,
57
52
  variant: parentVariant,
58
53
  as: "div",
59
54
  href: "#"
60
55
  }, parent), /*#__PURE__*/_react.default.createElement(_box.default, {
61
56
  overflowY: "scroll",
62
- scrollVariant: scrollVariants[menuContext.menuType],
57
+ scrollVariant: scrollVariants[menuType],
63
58
  height: height,
64
59
  p: 0,
65
60
  as: "ul",
66
61
  role: "list"
67
- }, _react.default.Children.map(children, (child, index) => {
68
- let isFocused = false;
69
- const blockItemFocused = focusIndex >= blockIndex;
70
-
71
- if (blockItemFocused) {
72
- isFocused = focusIndex - blockIndex === index;
73
- }
74
-
75
- return /*#__PURE__*/_react.default.createElement(_submenu.default.Provider, {
76
- value: {
77
- isFocused,
78
- handleKeyDown
79
- }
80
- }, child);
81
- })));
62
+ }, children));
82
63
  };
83
64
 
84
65
  ScrollableBlock.propTypes = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "114.12.3",
3
+ "version": "114.13.2",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",