@atlaskit/dropdown-menu 12.21.0 → 12.22.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.
Files changed (25) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/internal/components/focus-manager.js +1 -1
  3. package/dist/cjs/internal/components/menu-wrapper.js +14 -7
  4. package/dist/cjs/internal/hooks/use-register-item-with-focus-manager.js +2 -4
  5. package/dist/cjs/internal/utils/handle-focus.js +12 -11
  6. package/dist/cjs/internal/utils/use-generated-id.js +3 -11
  7. package/dist/es2019/internal/components/focus-manager.js +1 -1
  8. package/dist/es2019/internal/components/menu-wrapper.js +13 -5
  9. package/dist/es2019/internal/hooks/use-register-item-with-focus-manager.js +2 -7
  10. package/dist/es2019/internal/utils/handle-focus.js +17 -11
  11. package/dist/es2019/internal/utils/use-generated-id.js +3 -9
  12. package/dist/esm/internal/components/focus-manager.js +1 -1
  13. package/dist/esm/internal/components/menu-wrapper.js +14 -7
  14. package/dist/esm/internal/hooks/use-register-item-with-focus-manager.js +2 -5
  15. package/dist/esm/internal/utils/handle-focus.js +12 -11
  16. package/dist/esm/internal/utils/use-generated-id.js +3 -11
  17. package/dist/types/internal/components/focus-manager.d.ts +3 -3
  18. package/dist/types/internal/hooks/use-register-item-with-focus-manager.d.ts +1 -2
  19. package/dist/types/internal/utils/handle-focus.d.ts +3 -4
  20. package/dist/types/types.d.ts +1 -1
  21. package/dist/types-ts4.5/internal/components/focus-manager.d.ts +3 -3
  22. package/dist/types-ts4.5/internal/hooks/use-register-item-with-focus-manager.d.ts +1 -2
  23. package/dist/types-ts4.5/internal/utils/handle-focus.d.ts +3 -4
  24. package/dist/types-ts4.5/types.d.ts +1 -1
  25. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/dropdown-menu
2
2
 
3
+ ## 12.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`cfef14b7ec2bc`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/cfef14b7ec2bc) -
8
+ Update Dropdown Menu internals so that menuItemRefs are always stored in order. In the case of a
9
+ DropDownItem with async content then the ref may only be populated once the content has loaded.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 12.21.1
16
+
17
+ ### Patch Changes
18
+
19
+ - [#157534](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/157534)
20
+ [`ca0824645f3a5`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/ca0824645f3a5) -
21
+ Remove the use of Math.random for the generation of IDs
22
+
3
23
  ## 12.21.0
4
24
 
5
25
  ### Minor Changes
@@ -42,7 +42,7 @@ var FocusManager = function FocusManager(_ref) {
42
42
  var registerMode = (0, _react.useRef)('ordered');
43
43
  registerMode.current = 'ordered';
44
44
  var registerRef = (0, _react.useCallback)(function (ref) {
45
- if (!ref || menuItemRefs.current.includes(ref)) {
45
+ if (menuItemRefs.current.includes(ref)) {
46
46
  return;
47
47
  }
48
48
  switch (registerMode.current) {
@@ -10,9 +10,9 @@ var _react2 = require("@emotion/react");
10
10
  var _menuGroup = _interopRequireDefault(require("@atlaskit/menu/menu-group"));
11
11
  var _primitives = require("@atlaskit/primitives");
12
12
  var _spinner = _interopRequireDefault(require("@atlaskit/spinner"));
13
- var _focusManager = require("../components/focus-manager");
14
13
  var _isCheckboxItem = _interopRequireDefault(require("../utils/is-checkbox-item"));
15
14
  var _isRadioItem = _interopRequireDefault(require("../utils/is-radio-item"));
15
+ var _focusManager = require("./focus-manager");
16
16
  /**
17
17
  * @jsxRuntime classic
18
18
  * @jsx jsx
@@ -62,7 +62,11 @@ var MenuWrapper = function MenuWrapper(_ref2) {
62
62
  var _useContext = (0, _react.useContext)(_focusManager.FocusManagerContext),
63
63
  menuItemRefs = _useContext.menuItemRefs;
64
64
  var closeOnMenuItemClick = function closeOnMenuItemClick(e) {
65
- var isTargetMenuItemOrDecendant = menuItemRefs.some(function (menuItem) {
65
+ var isTargetMenuItemOrDescendant = menuItemRefs.some(function (menuItemRef) {
66
+ var menuItem = menuItemRef.current;
67
+ if (!menuItem) {
68
+ return false;
69
+ }
66
70
  var isCheckboxOrRadio = (0, _isCheckboxItem.default)(menuItem) || (0, _isRadioItem.default)(menuItem);
67
71
  return menuItem.contains(e.target) && !isCheckboxOrRadio;
68
72
  });
@@ -71,7 +75,7 @@ var MenuWrapper = function MenuWrapper(_ref2) {
71
75
  // its descendant. Don't close the menu if the click is triggered
72
76
  // from a MenuItemRadio or MenuItemCheckbox so that the user can
73
77
  // select multiple items.
74
- if (isTargetMenuItemOrDecendant && onClose) {
78
+ if (isTargetMenuItemOrDescendant && onClose) {
75
79
  onClose(e);
76
80
  }
77
81
  };
@@ -83,10 +87,13 @@ var MenuWrapper = function MenuWrapper(_ref2) {
83
87
  onUpdate();
84
88
  }, [isLoading, onUpdate]);
85
89
  (0, _react.useEffect)(function () {
86
- var _menuItemRefs$find;
87
- var firstFocusableRef = (_menuItemRefs$find = menuItemRefs.find(function (ref) {
88
- return !ref.hasAttribute('disabled');
89
- })) !== null && _menuItemRefs$find !== void 0 ? _menuItemRefs$find : null;
90
+ var _menuItemRefs$map$fin;
91
+ var firstFocusableRef = (_menuItemRefs$map$fin = menuItemRefs.map(function (_ref3) {
92
+ var current = _ref3.current;
93
+ return current;
94
+ }).find(function (el) {
95
+ return !!el && !el.hasAttribute('disabled');
96
+ })) !== null && _menuItemRefs$map$fin !== void 0 ? _menuItemRefs$map$fin : null;
90
97
  if (shouldRenderToParent && (isTriggeredUsingKeyboard || autoFocus)) {
91
98
  firstFocusableRef === null || firstFocusableRef === void 0 || firstFocusableRef.focus();
92
99
  }
@@ -7,16 +7,14 @@ exports.default = void 0;
7
7
  var _react = require("react");
8
8
  var _focusManager = require("../components/focus-manager");
9
9
  // This function is called whenever a MenuItem mounts.
10
- // The refs stored in the context are used to programatically
10
+ // The refs stored in the context are used to programmatically
11
11
  // control focus on a user navigates using the keyboard.
12
12
  function useRegisterItemWithFocusManager() {
13
13
  var _useContext = (0, _react.useContext)(_focusManager.FocusManagerContext),
14
14
  registerRef = _useContext.registerRef;
15
15
  var itemRef = (0, _react.useRef)(null);
16
16
  (0, _react.useEffect)(function () {
17
- if (itemRef.current !== null) {
18
- registerRef(itemRef.current);
19
- }
17
+ return registerRef(itemRef);
20
18
  }, [registerRef]);
21
19
  return itemRef;
22
20
  }
@@ -20,11 +20,11 @@ var actionMap = (_actionMap = {}, (0, _defineProperty2.default)(_actionMap, _key
20
20
  */
21
21
  var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
22
22
  while (currentFocusedIdx + 1 < refs.length) {
23
- var isDisabled = refs[currentFocusedIdx + 1].hasAttribute('disabled');
24
- if (!isDisabled) {
25
- return refs[currentFocusedIdx + 1];
23
+ var element = refs[++currentFocusedIdx].current;
24
+ var isValid = !!element && !element.hasAttribute('disabled');
25
+ if (isValid) {
26
+ return element;
26
27
  }
27
- currentFocusedIdx++;
28
28
  }
29
29
  };
30
30
 
@@ -36,20 +36,21 @@ var getNextFocusableElement = function getNextFocusableElement(refs, currentFocu
36
36
  */
37
37
  var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
38
38
  while (currentFocusedIdx > 0) {
39
- var isDisabled = refs[currentFocusedIdx - 1].hasAttribute('disabled');
40
- if (!isDisabled) {
41
- return refs[currentFocusedIdx - 1];
39
+ var element = refs[--currentFocusedIdx].current;
40
+ var isValid = !!element && !element.hasAttribute('disabled');
41
+ if (isValid) {
42
+ return element;
42
43
  }
43
- currentFocusedIdx--;
44
44
  }
45
45
  };
46
46
  function handleFocus(refs, isLayerDisabled, onClose) {
47
47
  return function (e) {
48
48
  var _refs$current;
49
- var currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : refs;
50
- var currentFocusedIdx = currentRefs.findIndex(function (el) {
49
+ var currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : [];
50
+ var currentFocusedIdx = currentRefs.findIndex(function (_ref) {
51
51
  var _document$activeEleme;
52
- return (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el);
52
+ var el = _ref.current;
53
+ return el && ((_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el));
53
54
  });
54
55
  if ((0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock')) {
55
56
  var _document$activeEleme2;
@@ -5,22 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.PREFIX = void 0;
7
7
  exports.default = useGeneratedId;
8
- var _react = require("react");
8
+ var _useId = require("@atlaskit/ds-lib/use-id");
9
9
  var PREFIX = exports.PREFIX = 'ds--dropdown--';
10
- var generateRandomString = function generateRandomString() {
11
- return (// This string is used only on client side usually triggered after a user interaction.
12
- // Therefore, so there is no risk of mismatch
13
- // between server and client generated markup.
14
- // eslint-disable-next-line @repo/internal/react/disallow-unstable-values
15
- "".concat(PREFIX).concat(Math.random().toString(16).substr(2, 8))
16
- );
17
- };
18
10
 
19
11
  /**
20
12
  * useGeneratedId generates a random string which remains constant across
21
13
  * renders when called without any parameter.
22
14
  */
23
15
  function useGeneratedId() {
24
- var cachedId = (0, _react.useRef)(generateRandomString());
25
- return cachedId.current;
16
+ var id = (0, _useId.useId)();
17
+ return "".concat(PREFIX).concat(id);
26
18
  }
@@ -30,7 +30,7 @@ const FocusManager = ({
30
30
  const registerMode = useRef('ordered');
31
31
  registerMode.current = 'ordered';
32
32
  const registerRef = useCallback(ref => {
33
- if (!ref || menuItemRefs.current.includes(ref)) {
33
+ if (menuItemRefs.current.includes(ref)) {
34
34
  return;
35
35
  }
36
36
  switch (registerMode.current) {
@@ -9,9 +9,9 @@ import { jsx } from '@emotion/react';
9
9
  import MenuGroup from '@atlaskit/menu/menu-group';
10
10
  import { Box, xcss } from '@atlaskit/primitives';
11
11
  import Spinner from '@atlaskit/spinner';
12
- import { FocusManagerContext } from '../components/focus-manager';
13
12
  import isCheckboxItem from '../utils/is-checkbox-item';
14
13
  import isRadioItem from '../utils/is-radio-item';
14
+ import { FocusManagerContext } from './focus-manager';
15
15
  const spinnerContainerStyles = xcss({
16
16
  display: 'flex',
17
17
  minWidth: '160px',
@@ -54,7 +54,13 @@ const MenuWrapper = ({
54
54
  menuItemRefs
55
55
  } = useContext(FocusManagerContext);
56
56
  const closeOnMenuItemClick = e => {
57
- const isTargetMenuItemOrDecendant = menuItemRefs.some(menuItem => {
57
+ const isTargetMenuItemOrDescendant = menuItemRefs.some(menuItemRef => {
58
+ const {
59
+ current: menuItem
60
+ } = menuItemRef;
61
+ if (!menuItem) {
62
+ return false;
63
+ }
58
64
  const isCheckboxOrRadio = isCheckboxItem(menuItem) || isRadioItem(menuItem);
59
65
  return menuItem.contains(e.target) && !isCheckboxOrRadio;
60
66
  });
@@ -63,7 +69,7 @@ const MenuWrapper = ({
63
69
  // its descendant. Don't close the menu if the click is triggered
64
70
  // from a MenuItemRadio or MenuItemCheckbox so that the user can
65
71
  // select multiple items.
66
- if (isTargetMenuItemOrDecendant && onClose) {
72
+ if (isTargetMenuItemOrDescendant && onClose) {
67
73
  onClose(e);
68
74
  }
69
75
  };
@@ -75,8 +81,10 @@ const MenuWrapper = ({
75
81
  onUpdate();
76
82
  }, [isLoading, onUpdate]);
77
83
  useEffect(() => {
78
- var _menuItemRefs$find;
79
- const firstFocusableRef = (_menuItemRefs$find = menuItemRefs.find(ref => !ref.hasAttribute('disabled'))) !== null && _menuItemRefs$find !== void 0 ? _menuItemRefs$find : null;
84
+ var _menuItemRefs$map$fin;
85
+ const firstFocusableRef = (_menuItemRefs$map$fin = menuItemRefs.map(({
86
+ current
87
+ }) => current).find(el => !!el && !el.hasAttribute('disabled'))) !== null && _menuItemRefs$map$fin !== void 0 ? _menuItemRefs$map$fin : null;
80
88
  if (shouldRenderToParent && (isTriggeredUsingKeyboard || autoFocus)) {
81
89
  firstFocusableRef === null || firstFocusableRef === void 0 ? void 0 : firstFocusableRef.focus();
82
90
  }
@@ -1,19 +1,14 @@
1
1
  import { useContext, useEffect, useRef } from 'react';
2
2
  import { FocusManagerContext } from '../components/focus-manager';
3
-
4
3
  // This function is called whenever a MenuItem mounts.
5
- // The refs stored in the context are used to programatically
4
+ // The refs stored in the context are used to programmatically
6
5
  // control focus on a user navigates using the keyboard.
7
6
  function useRegisterItemWithFocusManager() {
8
7
  const {
9
8
  registerRef
10
9
  } = useContext(FocusManagerContext);
11
10
  const itemRef = useRef(null);
12
- useEffect(() => {
13
- if (itemRef.current !== null) {
14
- registerRef(itemRef.current);
15
- }
16
- }, [registerRef]);
11
+ useEffect(() => registerRef(itemRef), [registerRef]);
17
12
  return itemRef;
18
13
  }
19
14
  export default useRegisterItemWithFocusManager;
@@ -16,11 +16,13 @@ const actionMap = {
16
16
  */
17
17
  const getNextFocusableElement = (refs, currentFocusedIdx) => {
18
18
  while (currentFocusedIdx + 1 < refs.length) {
19
- const isDisabled = refs[currentFocusedIdx + 1].hasAttribute('disabled');
20
- if (!isDisabled) {
21
- return refs[currentFocusedIdx + 1];
19
+ const {
20
+ current: element
21
+ } = refs[++currentFocusedIdx];
22
+ const isValid = !!element && !element.hasAttribute('disabled');
23
+ if (isValid) {
24
+ return element;
22
25
  }
23
- currentFocusedIdx++;
24
26
  }
25
27
  };
26
28
 
@@ -32,20 +34,24 @@ const getNextFocusableElement = (refs, currentFocusedIdx) => {
32
34
  */
33
35
  const getPrevFocusableElement = (refs, currentFocusedIdx) => {
34
36
  while (currentFocusedIdx > 0) {
35
- const isDisabled = refs[currentFocusedIdx - 1].hasAttribute('disabled');
36
- if (!isDisabled) {
37
- return refs[currentFocusedIdx - 1];
37
+ const {
38
+ current: element
39
+ } = refs[--currentFocusedIdx];
40
+ const isValid = !!element && !element.hasAttribute('disabled');
41
+ if (isValid) {
42
+ return element;
38
43
  }
39
- currentFocusedIdx--;
40
44
  }
41
45
  };
42
46
  export default function handleFocus(refs, isLayerDisabled, onClose) {
43
47
  return e => {
44
48
  var _refs$current;
45
- const currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : refs;
46
- const currentFocusedIdx = currentRefs.findIndex(el => {
49
+ const currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : [];
50
+ const currentFocusedIdx = currentRefs.findIndex(({
51
+ current: el
52
+ }) => {
47
53
  var _document$activeEleme;
48
- return (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el);
54
+ return el && ((_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el));
49
55
  });
50
56
  if (fg('platform_dst_popup-disable-focuslock')) {
51
57
  var _document$activeEleme2;
@@ -1,17 +1,11 @@
1
- import { useRef } from 'react';
1
+ import { useId } from '@atlaskit/ds-lib/use-id';
2
2
  export const PREFIX = 'ds--dropdown--';
3
- const generateRandomString = () =>
4
- // This string is used only on client side usually triggered after a user interaction.
5
- // Therefore, so there is no risk of mismatch
6
- // between server and client generated markup.
7
- // eslint-disable-next-line @repo/internal/react/disallow-unstable-values
8
- `${PREFIX}${Math.random().toString(16).substr(2, 8)}`;
9
3
 
10
4
  /**
11
5
  * useGeneratedId generates a random string which remains constant across
12
6
  * renders when called without any parameter.
13
7
  */
14
8
  export default function useGeneratedId() {
15
- const cachedId = useRef(generateRandomString());
16
- return cachedId.current;
9
+ const id = useId();
10
+ return `${PREFIX}${id}`;
17
11
  }
@@ -33,7 +33,7 @@ var FocusManager = function FocusManager(_ref) {
33
33
  var registerMode = useRef('ordered');
34
34
  registerMode.current = 'ordered';
35
35
  var registerRef = useCallback(function (ref) {
36
- if (!ref || menuItemRefs.current.includes(ref)) {
36
+ if (menuItemRefs.current.includes(ref)) {
37
37
  return;
38
38
  }
39
39
  switch (registerMode.current) {
@@ -9,9 +9,9 @@ import { jsx } from '@emotion/react';
9
9
  import MenuGroup from '@atlaskit/menu/menu-group';
10
10
  import { Box, xcss } from '@atlaskit/primitives';
11
11
  import Spinner from '@atlaskit/spinner';
12
- import { FocusManagerContext } from '../components/focus-manager';
13
12
  import isCheckboxItem from '../utils/is-checkbox-item';
14
13
  import isRadioItem from '../utils/is-radio-item';
14
+ import { FocusManagerContext } from './focus-manager';
15
15
  var spinnerContainerStyles = xcss({
16
16
  display: 'flex',
17
17
  minWidth: '160px',
@@ -54,7 +54,11 @@ var MenuWrapper = function MenuWrapper(_ref2) {
54
54
  var _useContext = useContext(FocusManagerContext),
55
55
  menuItemRefs = _useContext.menuItemRefs;
56
56
  var closeOnMenuItemClick = function closeOnMenuItemClick(e) {
57
- var isTargetMenuItemOrDecendant = menuItemRefs.some(function (menuItem) {
57
+ var isTargetMenuItemOrDescendant = menuItemRefs.some(function (menuItemRef) {
58
+ var menuItem = menuItemRef.current;
59
+ if (!menuItem) {
60
+ return false;
61
+ }
58
62
  var isCheckboxOrRadio = isCheckboxItem(menuItem) || isRadioItem(menuItem);
59
63
  return menuItem.contains(e.target) && !isCheckboxOrRadio;
60
64
  });
@@ -63,7 +67,7 @@ var MenuWrapper = function MenuWrapper(_ref2) {
63
67
  // its descendant. Don't close the menu if the click is triggered
64
68
  // from a MenuItemRadio or MenuItemCheckbox so that the user can
65
69
  // select multiple items.
66
- if (isTargetMenuItemOrDecendant && onClose) {
70
+ if (isTargetMenuItemOrDescendant && onClose) {
67
71
  onClose(e);
68
72
  }
69
73
  };
@@ -75,10 +79,13 @@ var MenuWrapper = function MenuWrapper(_ref2) {
75
79
  onUpdate();
76
80
  }, [isLoading, onUpdate]);
77
81
  useEffect(function () {
78
- var _menuItemRefs$find;
79
- var firstFocusableRef = (_menuItemRefs$find = menuItemRefs.find(function (ref) {
80
- return !ref.hasAttribute('disabled');
81
- })) !== null && _menuItemRefs$find !== void 0 ? _menuItemRefs$find : null;
82
+ var _menuItemRefs$map$fin;
83
+ var firstFocusableRef = (_menuItemRefs$map$fin = menuItemRefs.map(function (_ref3) {
84
+ var current = _ref3.current;
85
+ return current;
86
+ }).find(function (el) {
87
+ return !!el && !el.hasAttribute('disabled');
88
+ })) !== null && _menuItemRefs$map$fin !== void 0 ? _menuItemRefs$map$fin : null;
82
89
  if (shouldRenderToParent && (isTriggeredUsingKeyboard || autoFocus)) {
83
90
  firstFocusableRef === null || firstFocusableRef === void 0 || firstFocusableRef.focus();
84
91
  }
@@ -1,17 +1,14 @@
1
1
  import { useContext, useEffect, useRef } from 'react';
2
2
  import { FocusManagerContext } from '../components/focus-manager';
3
-
4
3
  // This function is called whenever a MenuItem mounts.
5
- // The refs stored in the context are used to programatically
4
+ // The refs stored in the context are used to programmatically
6
5
  // control focus on a user navigates using the keyboard.
7
6
  function useRegisterItemWithFocusManager() {
8
7
  var _useContext = useContext(FocusManagerContext),
9
8
  registerRef = _useContext.registerRef;
10
9
  var itemRef = useRef(null);
11
10
  useEffect(function () {
12
- if (itemRef.current !== null) {
13
- registerRef(itemRef.current);
14
- }
11
+ return registerRef(itemRef);
15
12
  }, [registerRef]);
16
13
  return itemRef;
17
14
  }
@@ -13,11 +13,11 @@ var actionMap = (_actionMap = {}, _defineProperty(_actionMap, KEY_DOWN, 'next'),
13
13
  */
14
14
  var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
15
15
  while (currentFocusedIdx + 1 < refs.length) {
16
- var isDisabled = refs[currentFocusedIdx + 1].hasAttribute('disabled');
17
- if (!isDisabled) {
18
- return refs[currentFocusedIdx + 1];
16
+ var element = refs[++currentFocusedIdx].current;
17
+ var isValid = !!element && !element.hasAttribute('disabled');
18
+ if (isValid) {
19
+ return element;
19
20
  }
20
- currentFocusedIdx++;
21
21
  }
22
22
  };
23
23
 
@@ -29,20 +29,21 @@ var getNextFocusableElement = function getNextFocusableElement(refs, currentFocu
29
29
  */
30
30
  var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
31
31
  while (currentFocusedIdx > 0) {
32
- var isDisabled = refs[currentFocusedIdx - 1].hasAttribute('disabled');
33
- if (!isDisabled) {
34
- return refs[currentFocusedIdx - 1];
32
+ var element = refs[--currentFocusedIdx].current;
33
+ var isValid = !!element && !element.hasAttribute('disabled');
34
+ if (isValid) {
35
+ return element;
35
36
  }
36
- currentFocusedIdx--;
37
37
  }
38
38
  };
39
39
  export default function handleFocus(refs, isLayerDisabled, onClose) {
40
40
  return function (e) {
41
41
  var _refs$current;
42
- var currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : refs;
43
- var currentFocusedIdx = currentRefs.findIndex(function (el) {
42
+ var currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : [];
43
+ var currentFocusedIdx = currentRefs.findIndex(function (_ref) {
44
44
  var _document$activeEleme;
45
- return (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el);
45
+ var el = _ref.current;
46
+ return el && ((_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el));
46
47
  });
47
48
  if (fg('platform_dst_popup-disable-focuslock')) {
48
49
  var _document$activeEleme2;
@@ -1,19 +1,11 @@
1
- import { useRef } from 'react';
1
+ import { useId } from '@atlaskit/ds-lib/use-id';
2
2
  export var PREFIX = 'ds--dropdown--';
3
- var generateRandomString = function generateRandomString() {
4
- return (// This string is used only on client side usually triggered after a user interaction.
5
- // Therefore, so there is no risk of mismatch
6
- // between server and client generated markup.
7
- // eslint-disable-next-line @repo/internal/react/disallow-unstable-values
8
- "".concat(PREFIX).concat(Math.random().toString(16).substr(2, 8))
9
- );
10
- };
11
3
 
12
4
  /**
13
5
  * useGeneratedId generates a random string which remains constant across
14
6
  * renders when called without any parameter.
15
7
  */
16
8
  export default function useGeneratedId() {
17
- var cachedId = useRef(generateRandomString());
18
- return cachedId.current;
9
+ var id = useId();
10
+ return "".concat(PREFIX).concat(id);
19
11
  }
@@ -1,5 +1,5 @@
1
1
  import React, { type FC, type ReactNode } from 'react';
2
- import { type FocusableElement } from '../../types';
2
+ import { type FocusableElementRef } from '../../types';
3
3
  /**
4
4
  *
5
5
  *
@@ -9,8 +9,8 @@ import { type FocusableElement } from '../../types';
9
9
  *
10
10
  */
11
11
  export declare const FocusManagerContext: React.Context<{
12
- menuItemRefs: FocusableElement[];
13
- registerRef: (ref: FocusableElement) => void;
12
+ menuItemRefs: FocusableElementRef[];
13
+ registerRef(ref: FocusableElementRef): void;
14
14
  }>;
15
15
  /**
16
16
  * Focus manager logic.
@@ -1,4 +1,3 @@
1
1
  /// <reference types="react" />
2
- import { type FocusableElement } from '../../types';
3
- declare function useRegisterItemWithFocusManager(): import("react").RefObject<FocusableElement>;
2
+ declare function useRegisterItemWithFocusManager(): import("react").MutableRefObject<HTMLAnchorElement | HTMLButtonElement | null>;
4
3
  export default useRegisterItemWithFocusManager;
@@ -1,4 +1,3 @@
1
- import { type FocusableElement } from '../../types';
2
- export default function handleFocus(refs: {
3
- current: Array<FocusableElement>;
4
- }, isLayerDisabled: () => boolean, onClose: (e: KeyboardEvent) => void): (e: KeyboardEvent) => void;
1
+ import { type RefObject } from 'react';
2
+ import { type FocusableElementRef } from '../../types';
3
+ export default function handleFocus(refs: RefObject<FocusableElementRef[]>, isLayerDisabled: () => boolean, onClose: (e: KeyboardEvent) => void): (e: KeyboardEvent) => void;
@@ -1,7 +1,7 @@
1
1
  import { type KeyboardEvent, type MouseEvent, type ReactElement, type ReactNode, type Ref, type RefObject } from 'react';
2
2
  import type { CustomItemComponentProps, CustomItemProps, MenuGroupProps, SectionProps } from '@atlaskit/menu/types';
3
3
  import type { ContentProps, TriggerProps } from '@atlaskit/popup/types';
4
- export type FocusableElement = HTMLAnchorElement | HTMLButtonElement;
4
+ export type FocusableElementRef = RefObject<HTMLAnchorElement | HTMLButtonElement>;
5
5
  export type Action = 'next' | 'prev' | 'first' | 'last' | 'tab';
6
6
  export type Placement = 'auto-start' | 'auto' | 'auto-end' | 'top-start' | 'top' | 'top-end' | 'right-start' | 'right' | 'right-end' | 'bottom-end' | 'bottom' | 'bottom-start' | 'left-end' | 'left' | 'left-start';
7
7
  export type ItemId = string;
@@ -1,5 +1,5 @@
1
1
  import React, { type FC, type ReactNode } from 'react';
2
- import { type FocusableElement } from '../../types';
2
+ import { type FocusableElementRef } from '../../types';
3
3
  /**
4
4
  *
5
5
  *
@@ -9,8 +9,8 @@ import { type FocusableElement } from '../../types';
9
9
  *
10
10
  */
11
11
  export declare const FocusManagerContext: React.Context<{
12
- menuItemRefs: FocusableElement[];
13
- registerRef: (ref: FocusableElement) => void;
12
+ menuItemRefs: FocusableElementRef[];
13
+ registerRef(ref: FocusableElementRef): void;
14
14
  }>;
15
15
  /**
16
16
  * Focus manager logic.
@@ -1,4 +1,3 @@
1
1
  /// <reference types="react" />
2
- import { type FocusableElement } from '../../types';
3
- declare function useRegisterItemWithFocusManager(): import("react").RefObject<FocusableElement>;
2
+ declare function useRegisterItemWithFocusManager(): import("react").MutableRefObject<HTMLAnchorElement | HTMLButtonElement | null>;
4
3
  export default useRegisterItemWithFocusManager;
@@ -1,4 +1,3 @@
1
- import { type FocusableElement } from '../../types';
2
- export default function handleFocus(refs: {
3
- current: Array<FocusableElement>;
4
- }, isLayerDisabled: () => boolean, onClose: (e: KeyboardEvent) => void): (e: KeyboardEvent) => void;
1
+ import { type RefObject } from 'react';
2
+ import { type FocusableElementRef } from '../../types';
3
+ export default function handleFocus(refs: RefObject<FocusableElementRef[]>, isLayerDisabled: () => boolean, onClose: (e: KeyboardEvent) => void): (e: KeyboardEvent) => void;
@@ -1,7 +1,7 @@
1
1
  import { type KeyboardEvent, type MouseEvent, type ReactElement, type ReactNode, type Ref, type RefObject } from 'react';
2
2
  import type { CustomItemComponentProps, CustomItemProps, MenuGroupProps, SectionProps } from '@atlaskit/menu/types';
3
3
  import type { ContentProps, TriggerProps } from '@atlaskit/popup/types';
4
- export type FocusableElement = HTMLAnchorElement | HTMLButtonElement;
4
+ export type FocusableElementRef = RefObject<HTMLAnchorElement | HTMLButtonElement>;
5
5
  export type Action = 'next' | 'prev' | 'first' | 'last' | 'tab';
6
6
  export type Placement = 'auto-start' | 'auto' | 'auto-end' | 'top-start' | 'top' | 'top-end' | 'right-start' | 'right' | 'right-end' | 'bottom-end' | 'bottom' | 'bottom-start' | 'left-end' | 'left' | 'left-start';
7
7
  export type ItemId = string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/dropdown-menu",
3
- "version": "12.21.0",
3
+ "version": "12.22.0",
4
4
  "description": "A dropdown menu displays a list of actions or options to a user.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -26,12 +26,12 @@
26
26
  "@atlaskit/button": "^20.3.0",
27
27
  "@atlaskit/codemod-utils": "^4.2.0",
28
28
  "@atlaskit/ds-lib": "^3.1.0",
29
- "@atlaskit/icon": "^22.23.0",
29
+ "@atlaskit/icon": "^22.24.0",
30
30
  "@atlaskit/layering": "^0.7.0",
31
31
  "@atlaskit/menu": "^2.13.0",
32
32
  "@atlaskit/platform-feature-flags": "^0.3.0",
33
33
  "@atlaskit/popup": "^1.29.0",
34
- "@atlaskit/primitives": "^12.2.0",
34
+ "@atlaskit/primitives": "^13.0.0",
35
35
  "@atlaskit/spinner": "^16.3.0",
36
36
  "@atlaskit/theme": "^14.0.0",
37
37
  "@atlaskit/tokens": "^2.0.0",