carbon-react 143.2.3 → 143.2.4

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.
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ declare type ReactItem = React.ReactChild | React.ReactFragment | React.ReactPortal;
3
+ export declare const getItems: (children: React.ReactNode | React.ReactNode[]) => ReactItem[];
4
+ export declare const isItemDisabled: (item: ReactItem) => boolean;
5
+ export declare const findFirstFocusableItem: (items: ReactItem[]) => number;
6
+ export declare const findLastFocusableItem: (items: ReactItem[]) => number;
7
+ export {};
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { ActionPopoverItem } from "../action-popover-item/action-popover-item.component";
3
+
4
+ // Reusable type alias for item types
5
+
6
+ export const getItems = children => React.Children.toArray(children).filter(child => /*#__PURE__*/React.isValidElement(child) && child.type === ActionPopoverItem);
7
+ export const isItemDisabled = item => /*#__PURE__*/React.isValidElement(item) && !!item.props?.disabled;
8
+ export const findFirstFocusableItem = items => items.findIndex((_, index) => !isItemDisabled(items[index]));
9
+
10
+ // FIX-ME: FE-6248
11
+ // Once we no longer support Node 16, this function can be removed and `findLastIndex()` can be used in its place.
12
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex
13
+ export const findLastFocusableItem = items => {
14
+ for (let i = items.length - 1; i >= 0; i--) {
15
+ if (!isItemDisabled(items[i])) {
16
+ return i;
17
+ }
18
+ }
19
+ return -1;
20
+ };
@@ -110,12 +110,13 @@ export const ActionPopoverItem = ({
110
110
  }
111
111
  }, [alignSubmenu, submenu]);
112
112
 
113
- // focuses item on opening of actionPopover submenu
113
+ // Focuses item on opening of actionPopover submenu, but we want to do this once the Popover has finished opening
114
+ // We always want the focused item to be in the user's view for accessibility purposes, and without the initial unexpected scroll to top of page when used in a table.
114
115
  useEffect(() => {
115
116
  if (focusItem) {
116
- ref.current?.focus({
117
- preventScroll: true
118
- });
117
+ setTimeout(() => {
118
+ ref.current?.focus();
119
+ }, 0);
119
120
  }
120
121
  }, [focusItem]);
121
122
  useEffect(() => {
@@ -1,5 +1,5 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
- import React, { useCallback, useMemo, useContext, useState, useEffect } from "react";
2
+ import React, { useCallback, useMemo, useContext, useState } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import invariant from "invariant";
5
5
  import { Menu } from "../action-popover.style";
@@ -7,6 +7,7 @@ import Events from "../../../__internal__/utils/helpers/events";
7
7
  import ActionPopoverItem from "../action-popover-item/action-popover-item.component";
8
8
  import ActionPopoverDivider from "../action-popover-divider/action-popover-divider.component";
9
9
  import ActionPopoverContext from "../__internal__/action-popover.context";
10
+ import { findFirstFocusableItem, findLastFocusableItem, getItems, isItemDisabled } from "../__internal__/action-popover-utils";
10
11
  const ActionPopoverMenu = /*#__PURE__*/React.forwardRef(({
11
12
  children,
12
13
  parentID,
@@ -37,36 +38,10 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef(({
37
38
  return !incorrectChild;
38
39
  }, [children]);
39
40
  !hasProperChildren ? process.env.NODE_ENV !== "production" ? invariant(false, `ActionPopoverMenu only accepts children of type \`${ActionPopoverItem.displayName}\`` + ` and \`${ActionPopoverDivider.displayName}\`.`) : invariant(false) : void 0;
40
- const items = useMemo(() => {
41
- return React.Children.toArray(children).filter(child => {
42
- return /*#__PURE__*/React.isValidElement(child) && child.type === ActionPopoverItem;
43
- });
44
- }, [children]);
45
- const isItemDisabled = useCallback(value => {
46
- const item = items[value];
47
- // The invariant will be triggered before this else path can be explored, hence the ignore else.
48
- // istanbul ignore else
49
- return /*#__PURE__*/React.isValidElement(item) && item.props.disabled;
50
- }, [items]);
51
- const firstFocusableItem = items.findIndex((_, index) => !isItemDisabled(index));
52
-
53
- // FIX-ME: FE-6248
54
- // Once we no longer support Node 16, this function can be removed and `findLastIndex()` can be used in it's place.
55
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex
56
- function findLastFocusableItem() {
57
- let lastFocusableItem = -1;
58
- for (let i = items.length - 1; i >= 0; i--) {
59
- if (!isItemDisabled(i)) {
60
- lastFocusableItem = i;
61
- break;
62
- }
63
- }
64
- return lastFocusableItem;
65
- }
66
- const lastFocusableItem = findLastFocusableItem();
67
- useEffect(() => {
68
- if (isOpen && firstFocusableItem !== -1) setFocusIndex(firstFocusableItem);
69
- }, [isOpen, firstFocusableItem, setFocusIndex]);
41
+ const items = useMemo(() => getItems(children), [children]);
42
+ const checkItemDisabled = useCallback(value => isItemDisabled(items[value]), [items]);
43
+ const firstFocusableItem = findFirstFocusableItem(items);
44
+ const lastFocusableItem = findLastFocusableItem(items);
70
45
  const onKeyDown = useCallback(e => {
71
46
  if (Events.isTabKey(e)) {
72
47
  e.preventDefault();
@@ -78,7 +53,7 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef(({
78
53
  e.preventDefault();
79
54
  e.stopPropagation();
80
55
  let indexValue = focusIndex + 1;
81
- while (indexValue < items.length && isItemDisabled(indexValue)) {
56
+ while (indexValue < items.length && checkItemDisabled(indexValue)) {
82
57
  indexValue += 1;
83
58
  }
84
59
  if (indexValue >= items.length) {
@@ -90,7 +65,7 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef(({
90
65
  e.preventDefault();
91
66
  e.stopPropagation();
92
67
  let indexValue = focusIndex - 1;
93
- while (indexValue >= firstFocusableItem && isItemDisabled(indexValue)) {
68
+ while (indexValue >= firstFocusableItem && checkItemDisabled(indexValue)) {
94
69
  indexValue -= 1;
95
70
  }
96
71
  if (indexValue < firstFocusableItem) {
@@ -116,7 +91,7 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef(({
116
91
  let firstMatch;
117
92
  let nextMatch;
118
93
  items.forEach((item, index) => {
119
- if (/*#__PURE__*/React.isValidElement(item) && !isItemDisabled(index) && item.props.children.toLowerCase().startsWith(e.key.toLowerCase())) {
94
+ if (/*#__PURE__*/React.isValidElement(item) && !checkItemDisabled(index) && item.props.children.toLowerCase().startsWith(e.key.toLowerCase())) {
120
95
  // istanbul ignore else
121
96
  if (firstMatch === undefined) {
122
97
  firstMatch = index;
@@ -132,7 +107,7 @@ const ActionPopoverMenu = /*#__PURE__*/React.forwardRef(({
132
107
  setFocusIndex(firstMatch);
133
108
  }
134
109
  }
135
- }, [focusButton, setOpen, focusIndex, items, isItemDisabled, setFocusIndex, firstFocusableItem, lastFocusableItem]);
110
+ }, [focusButton, setOpen, focusIndex, items, checkItemDisabled, setFocusIndex, firstFocusableItem, lastFocusableItem]);
136
111
  const [childHasSubmenu, setChildHasSubmenu] = useState(false);
137
112
  const [childHasIcon, setChildHasIcon] = useState(false);
138
113
  const [currentSubmenuPosition, setCurrentSubmenuPosition] = useState(submenuPosition);
@@ -12,6 +12,7 @@ import ActionPopoverItem from "./action-popover-item/action-popover-item.compone
12
12
  import ActionPopoverDivider from "./action-popover-divider/action-popover-divider.component";
13
13
  import ActionPopoverContext from "./__internal__/action-popover.context";
14
14
  import useModalManager from "../../hooks/__internal__/useModalManager";
15
+ import { findFirstFocusableItem, findLastFocusableItem, getItems } from "./__internal__/action-popover-utils";
15
16
  const onOpenDefault = () => {};
16
17
  const onCloseDefault = () => {};
17
18
  export const ActionPopover = ({
@@ -33,11 +34,6 @@ export const ActionPopover = ({
33
34
  const [guid] = useState(createGuid());
34
35
  const buttonRef = useRef(null);
35
36
  const menu = useRef(null);
36
- const itemCount = useMemo(() => {
37
- return React.Children.toArray(children).filter(child => {
38
- return /*#__PURE__*/React.isValidElement(child) && child.type === ActionPopoverItem;
39
- }).length;
40
- }, [children]);
41
37
  const hasProperChildren = useMemo(() => {
42
38
  const incorrectChild = React.Children.toArray(children).find(child => {
43
39
  if (! /*#__PURE__*/React.isValidElement(child)) {
@@ -47,6 +43,9 @@ export const ActionPopover = ({
47
43
  });
48
44
  return !incorrectChild;
49
45
  }, [children]);
46
+ const items = useMemo(() => getItems(children), [children]);
47
+ const firstFocusableItem = findFirstFocusableItem(items);
48
+ const lastFocusableItem = findLastFocusableItem(items);
50
49
  !hasProperChildren ? process.env.NODE_ENV !== "production" ? invariant(false, `ActionPopover only accepts children of type \`${ActionPopoverItem.displayName}\`` + ` and \`${ActionPopoverDivider.displayName}\`.`) : invariant(false) : void 0;
51
50
  const mappedPlacement = useMemo(() => {
52
51
  if (placement === "top" && !rightAlignMenu) {
@@ -76,12 +75,13 @@ export const ActionPopover = ({
76
75
  const onButtonClick = useCallback(e => {
77
76
  e.stopPropagation();
78
77
  const isOpening = !isOpen;
78
+ setFocusIndex(firstFocusableItem);
79
79
  setOpen(isOpening);
80
80
  if (!isOpening) {
81
81
  // Closing the menu should focus the MenuButton
82
82
  focusButton();
83
83
  }
84
- }, [isOpen, setOpen, focusButton]);
84
+ }, [isOpen, firstFocusableItem, setOpen, focusButton]);
85
85
 
86
86
  // Keyboard commands implemented as recommended by WAI-ARIA best practices
87
87
  // https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-actions.html
@@ -90,15 +90,15 @@ export const ActionPopover = ({
90
90
  if (Events.isSpaceKey(e) || Events.isDownKey(e) || Events.isEnterKey(e)) {
91
91
  e.preventDefault();
92
92
  e.stopPropagation();
93
- setFocusIndex(0);
93
+ setFocusIndex(firstFocusableItem);
94
94
  setOpen(true);
95
95
  } else if (Events.isUpKey(e)) {
96
96
  e.preventDefault();
97
97
  e.stopPropagation();
98
- setFocusIndex(itemCount - 1);
98
+ setFocusIndex(lastFocusableItem);
99
99
  setOpen(true);
100
100
  }
101
- }, [itemCount, setOpen]);
101
+ }, [firstFocusableItem, lastFocusableItem, setOpen]);
102
102
  const handleEscapeKey = useCallback(e => {
103
103
  /* istanbul ignore else */
104
104
  if (Events.isEscKey(e)) {
@@ -115,7 +115,7 @@ export const ActionPopover = ({
115
115
  const handler = ({
116
116
  target
117
117
  }) => {
118
- // If the event didn't came from part of this component, close the menu.
118
+ // If the event didn't come from part of this component, close the menu.
119
119
  // There will be multiple document click listeners but we cant prevent propagation because it will interfere with
120
120
  // other instances on the same page
121
121
 
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ declare type ReactItem = React.ReactChild | React.ReactFragment | React.ReactPortal;
3
+ export declare const getItems: (children: React.ReactNode | React.ReactNode[]) => ReactItem[];
4
+ export declare const isItemDisabled: (item: ReactItem) => boolean;
5
+ export declare const findFirstFocusableItem: (items: ReactItem[]) => number;
6
+ export declare const findLastFocusableItem: (items: ReactItem[]) => number;
7
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isItemDisabled = exports.getItems = exports.findLastFocusableItem = exports.findFirstFocusableItem = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _actionPopoverItem = require("../action-popover-item/action-popover-item.component");
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ // Reusable type alias for item types
11
+
12
+ const getItems = children => _react.default.Children.toArray(children).filter(child => /*#__PURE__*/_react.default.isValidElement(child) && child.type === _actionPopoverItem.ActionPopoverItem);
13
+ exports.getItems = getItems;
14
+ const isItemDisabled = item => /*#__PURE__*/_react.default.isValidElement(item) && !!item.props?.disabled;
15
+ exports.isItemDisabled = isItemDisabled;
16
+ const findFirstFocusableItem = items => items.findIndex((_, index) => !isItemDisabled(items[index]));
17
+
18
+ // FIX-ME: FE-6248
19
+ // Once we no longer support Node 16, this function can be removed and `findLastIndex()` can be used in its place.
20
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex
21
+ exports.findFirstFocusableItem = findFirstFocusableItem;
22
+ const findLastFocusableItem = items => {
23
+ for (let i = items.length - 1; i >= 0; i--) {
24
+ if (!isItemDisabled(items[i])) {
25
+ return i;
26
+ }
27
+ }
28
+ return -1;
29
+ };
30
+ exports.findLastFocusableItem = findLastFocusableItem;
@@ -119,12 +119,13 @@ const ActionPopoverItem = ({
119
119
  }
120
120
  }, [alignSubmenu, submenu]);
121
121
 
122
- // focuses item on opening of actionPopover submenu
122
+ // Focuses item on opening of actionPopover submenu, but we want to do this once the Popover has finished opening
123
+ // We always want the focused item to be in the user's view for accessibility purposes, and without the initial unexpected scroll to top of page when used in a table.
123
124
  (0, _react.useEffect)(() => {
124
125
  if (focusItem) {
125
- ref.current?.focus({
126
- preventScroll: true
127
- });
126
+ setTimeout(() => {
127
+ ref.current?.focus();
128
+ }, 0);
128
129
  }
129
130
  }, [focusItem]);
130
131
  (0, _react.useEffect)(() => {
@@ -12,6 +12,7 @@ var _events = _interopRequireDefault(require("../../../__internal__/utils/helper
12
12
  var _actionPopoverItem = _interopRequireDefault(require("../action-popover-item/action-popover-item.component"));
13
13
  var _actionPopoverDivider = _interopRequireDefault(require("../action-popover-divider/action-popover-divider.component"));
14
14
  var _actionPopover2 = _interopRequireDefault(require("../__internal__/action-popover.context"));
15
+ var _actionPopoverUtils = require("../__internal__/action-popover-utils");
15
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
17
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
17
18
  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; }
@@ -46,36 +47,10 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef(({
46
47
  return !incorrectChild;
47
48
  }, [children]);
48
49
  !hasProperChildren ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, `ActionPopoverMenu only accepts children of type \`${_actionPopoverItem.default.displayName}\`` + ` and \`${_actionPopoverDivider.default.displayName}\`.`) : (0, _invariant.default)(false) : void 0;
49
- const items = (0, _react.useMemo)(() => {
50
- return _react.default.Children.toArray(children).filter(child => {
51
- return /*#__PURE__*/_react.default.isValidElement(child) && child.type === _actionPopoverItem.default;
52
- });
53
- }, [children]);
54
- const isItemDisabled = (0, _react.useCallback)(value => {
55
- const item = items[value];
56
- // The invariant will be triggered before this else path can be explored, hence the ignore else.
57
- // istanbul ignore else
58
- return /*#__PURE__*/_react.default.isValidElement(item) && item.props.disabled;
59
- }, [items]);
60
- const firstFocusableItem = items.findIndex((_, index) => !isItemDisabled(index));
61
-
62
- // FIX-ME: FE-6248
63
- // Once we no longer support Node 16, this function can be removed and `findLastIndex()` can be used in it's place.
64
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex
65
- function findLastFocusableItem() {
66
- let lastFocusableItem = -1;
67
- for (let i = items.length - 1; i >= 0; i--) {
68
- if (!isItemDisabled(i)) {
69
- lastFocusableItem = i;
70
- break;
71
- }
72
- }
73
- return lastFocusableItem;
74
- }
75
- const lastFocusableItem = findLastFocusableItem();
76
- (0, _react.useEffect)(() => {
77
- if (isOpen && firstFocusableItem !== -1) setFocusIndex(firstFocusableItem);
78
- }, [isOpen, firstFocusableItem, setFocusIndex]);
50
+ const items = (0, _react.useMemo)(() => (0, _actionPopoverUtils.getItems)(children), [children]);
51
+ const checkItemDisabled = (0, _react.useCallback)(value => (0, _actionPopoverUtils.isItemDisabled)(items[value]), [items]);
52
+ const firstFocusableItem = (0, _actionPopoverUtils.findFirstFocusableItem)(items);
53
+ const lastFocusableItem = (0, _actionPopoverUtils.findLastFocusableItem)(items);
79
54
  const onKeyDown = (0, _react.useCallback)(e => {
80
55
  if (_events.default.isTabKey(e)) {
81
56
  e.preventDefault();
@@ -87,7 +62,7 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef(({
87
62
  e.preventDefault();
88
63
  e.stopPropagation();
89
64
  let indexValue = focusIndex + 1;
90
- while (indexValue < items.length && isItemDisabled(indexValue)) {
65
+ while (indexValue < items.length && checkItemDisabled(indexValue)) {
91
66
  indexValue += 1;
92
67
  }
93
68
  if (indexValue >= items.length) {
@@ -99,7 +74,7 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef(({
99
74
  e.preventDefault();
100
75
  e.stopPropagation();
101
76
  let indexValue = focusIndex - 1;
102
- while (indexValue >= firstFocusableItem && isItemDisabled(indexValue)) {
77
+ while (indexValue >= firstFocusableItem && checkItemDisabled(indexValue)) {
103
78
  indexValue -= 1;
104
79
  }
105
80
  if (indexValue < firstFocusableItem) {
@@ -125,7 +100,7 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef(({
125
100
  let firstMatch;
126
101
  let nextMatch;
127
102
  items.forEach((item, index) => {
128
- if (/*#__PURE__*/_react.default.isValidElement(item) && !isItemDisabled(index) && item.props.children.toLowerCase().startsWith(e.key.toLowerCase())) {
103
+ if (/*#__PURE__*/_react.default.isValidElement(item) && !checkItemDisabled(index) && item.props.children.toLowerCase().startsWith(e.key.toLowerCase())) {
129
104
  // istanbul ignore else
130
105
  if (firstMatch === undefined) {
131
106
  firstMatch = index;
@@ -141,7 +116,7 @@ const ActionPopoverMenu = /*#__PURE__*/_react.default.forwardRef(({
141
116
  setFocusIndex(firstMatch);
142
117
  }
143
118
  }
144
- }, [focusButton, setOpen, focusIndex, items, isItemDisabled, setFocusIndex, firstFocusableItem, lastFocusableItem]);
119
+ }, [focusButton, setOpen, focusIndex, items, checkItemDisabled, setFocusIndex, firstFocusableItem, lastFocusableItem]);
145
120
  const [childHasSubmenu, setChildHasSubmenu] = (0, _react.useState)(false);
146
121
  const [childHasIcon, setChildHasIcon] = (0, _react.useState)(false);
147
122
  const [currentSubmenuPosition, setCurrentSubmenuPosition] = (0, _react.useState)(submenuPosition);
@@ -17,6 +17,7 @@ var _actionPopoverItem = _interopRequireDefault(require("./action-popover-item/a
17
17
  var _actionPopoverDivider = _interopRequireDefault(require("./action-popover-divider/action-popover-divider.component"));
18
18
  var _actionPopover2 = _interopRequireDefault(require("./__internal__/action-popover.context"));
19
19
  var _useModalManager = _interopRequireDefault(require("../../hooks/__internal__/useModalManager"));
20
+ var _actionPopoverUtils = require("./__internal__/action-popover-utils");
20
21
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
22
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
22
23
  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; }
@@ -42,11 +43,6 @@ const ActionPopover = ({
42
43
  const [guid] = (0, _react.useState)((0, _guid.default)());
43
44
  const buttonRef = (0, _react.useRef)(null);
44
45
  const menu = (0, _react.useRef)(null);
45
- const itemCount = (0, _react.useMemo)(() => {
46
- return _react.default.Children.toArray(children).filter(child => {
47
- return /*#__PURE__*/_react.default.isValidElement(child) && child.type === _actionPopoverItem.default;
48
- }).length;
49
- }, [children]);
50
46
  const hasProperChildren = (0, _react.useMemo)(() => {
51
47
  const incorrectChild = _react.default.Children.toArray(children).find(child => {
52
48
  if (! /*#__PURE__*/_react.default.isValidElement(child)) {
@@ -56,6 +52,9 @@ const ActionPopover = ({
56
52
  });
57
53
  return !incorrectChild;
58
54
  }, [children]);
55
+ const items = (0, _react.useMemo)(() => (0, _actionPopoverUtils.getItems)(children), [children]);
56
+ const firstFocusableItem = (0, _actionPopoverUtils.findFirstFocusableItem)(items);
57
+ const lastFocusableItem = (0, _actionPopoverUtils.findLastFocusableItem)(items);
59
58
  !hasProperChildren ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, `ActionPopover only accepts children of type \`${_actionPopoverItem.default.displayName}\`` + ` and \`${_actionPopoverDivider.default.displayName}\`.`) : (0, _invariant.default)(false) : void 0;
60
59
  const mappedPlacement = (0, _react.useMemo)(() => {
61
60
  if (placement === "top" && !rightAlignMenu) {
@@ -85,12 +84,13 @@ const ActionPopover = ({
85
84
  const onButtonClick = (0, _react.useCallback)(e => {
86
85
  e.stopPropagation();
87
86
  const isOpening = !isOpen;
87
+ setFocusIndex(firstFocusableItem);
88
88
  setOpen(isOpening);
89
89
  if (!isOpening) {
90
90
  // Closing the menu should focus the MenuButton
91
91
  focusButton();
92
92
  }
93
- }, [isOpen, setOpen, focusButton]);
93
+ }, [isOpen, firstFocusableItem, setOpen, focusButton]);
94
94
 
95
95
  // Keyboard commands implemented as recommended by WAI-ARIA best practices
96
96
  // https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-actions.html
@@ -99,15 +99,15 @@ const ActionPopover = ({
99
99
  if (_events.default.isSpaceKey(e) || _events.default.isDownKey(e) || _events.default.isEnterKey(e)) {
100
100
  e.preventDefault();
101
101
  e.stopPropagation();
102
- setFocusIndex(0);
102
+ setFocusIndex(firstFocusableItem);
103
103
  setOpen(true);
104
104
  } else if (_events.default.isUpKey(e)) {
105
105
  e.preventDefault();
106
106
  e.stopPropagation();
107
- setFocusIndex(itemCount - 1);
107
+ setFocusIndex(lastFocusableItem);
108
108
  setOpen(true);
109
109
  }
110
- }, [itemCount, setOpen]);
110
+ }, [firstFocusableItem, lastFocusableItem, setOpen]);
111
111
  const handleEscapeKey = (0, _react.useCallback)(e => {
112
112
  /* istanbul ignore else */
113
113
  if (_events.default.isEscKey(e)) {
@@ -124,7 +124,7 @@ const ActionPopover = ({
124
124
  const handler = ({
125
125
  target
126
126
  }) => {
127
- // If the event didn't came from part of this component, close the menu.
127
+ // If the event didn't come from part of this component, close the menu.
128
128
  // There will be multiple document click listeners but we cant prevent propagation because it will interfere with
129
129
  // other instances on the same page
130
130
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "143.2.3",
3
+ "version": "143.2.4",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",