@atlaskit/dropdown-menu 11.5.4 → 11.5.6

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @atlaskit/dropdown-menu
2
2
 
3
+ ## 11.5.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6ae8910147b`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6ae8910147b) - [ux] Allow users to navigate past disabled menu items with arrow keys
8
+
9
+ ## 11.5.5
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 11.5.4
16
+
17
+ ### Patch Changes
18
+
19
+ - [`ab6ea02a97e`](https://bitbucket.org/atlassian/atlassian-frontend/commits/ab6ea02a97e) - Align internal implementation with current linting rules. There should be no change for consumers.
20
+
3
21
  ## 11.5.4
4
22
 
5
23
  ### Patch Changes
@@ -1,41 +1,44 @@
1
1
  import core from 'jscodeshift';
2
2
  import { Collection } from 'jscodeshift/src/Collection';
3
3
 
4
- export const createRenameImportFor = ({
5
- componentName,
6
- newComponentName,
7
- packagePath,
8
- isDefaultImport,
9
- }: {
10
- componentName: string;
11
- newComponentName: string;
12
- packagePath: string;
13
- isDefaultImport: boolean;
14
- }) => (j: core.JSCodeshift, source: Collection<Node>) => {
15
- source
16
- .find(j.ImportDeclaration)
17
- .filter((path) => path.node.source.value === packagePath)
18
- .find(j.ImportSpecifier)
19
- .filter((path) => path.node.imported.name === componentName)
20
- .replaceWith((importSpecifier) => {
21
- const specifier = j.identifier(newComponentName);
4
+ export const createRenameImportFor =
5
+ ({
6
+ componentName,
7
+ newComponentName,
8
+ packagePath,
9
+ isDefaultImport,
10
+ }: {
11
+ componentName: string;
12
+ newComponentName: string;
13
+ packagePath: string;
14
+ isDefaultImport: boolean;
15
+ }) =>
16
+ (j: core.JSCodeshift, source: Collection<Node>) => {
17
+ source
18
+ .find(j.ImportDeclaration)
19
+ .filter((path) => path.node.source.value === packagePath)
20
+ .find(j.ImportSpecifier)
21
+ .filter((path) => path.node.imported.name === componentName)
22
+ .replaceWith((importSpecifier) => {
23
+ const specifier = j.identifier(newComponentName);
22
24
 
23
- let importAlias;
24
- if (
25
- importSpecifier &&
26
- importSpecifier.node &&
27
- importSpecifier.node.local
28
- ) {
29
- importAlias =
30
- importSpecifier.node.local.name !== importSpecifier.node.imported.name
31
- ? j.identifier(importSpecifier.node.local.name)
32
- : null;
33
- }
25
+ let importAlias;
26
+ if (
27
+ importSpecifier &&
28
+ importSpecifier.node &&
29
+ importSpecifier.node.local
30
+ ) {
31
+ importAlias =
32
+ importSpecifier.node.local.name !==
33
+ importSpecifier.node.imported.name
34
+ ? j.identifier(importSpecifier.node.local.name)
35
+ : null;
36
+ }
34
37
 
35
- return isDefaultImport
36
- ? j.importDefaultSpecifier(importAlias || specifier)
37
- : j.importSpecifier(specifier, importAlias);
38
- });
38
+ return isDefaultImport
39
+ ? j.importDefaultSpecifier(importAlias || specifier)
40
+ : j.importSpecifier(specifier, importAlias);
41
+ });
39
42
 
40
- return source.toSource();
41
- };
43
+ return source.toSource();
44
+ };
@@ -1,32 +1,34 @@
1
1
  import core from 'jscodeshift';
2
2
  import { Collection } from 'jscodeshift/src/Collection';
3
3
 
4
- export const createUpdateCallsite = ({
5
- componentName,
6
- newComponentName,
7
- packagePath,
8
- }: {
9
- componentName: string;
10
- newComponentName: string;
11
- packagePath: string;
12
- }) => (j: core.JSCodeshift, source: Collection<Node>) => {
13
- source
14
- .find(j.JSXElement)
15
- // @ts-ignore
16
- .filter((path) => path.node.openingElement.name.name === componentName)
17
- .replaceWith((element) => {
18
- const elementName = j.jsxIdentifier(newComponentName);
19
- const newComponent = j.jsxElement(
20
- j.jsxOpeningElement(
21
- elementName,
22
- element.node.openingElement.attributes,
23
- ),
24
- j.jsxClosingElement(elementName),
25
- element.node.children,
26
- );
4
+ export const createUpdateCallsite =
5
+ ({
6
+ componentName,
7
+ newComponentName,
8
+ packagePath,
9
+ }: {
10
+ componentName: string;
11
+ newComponentName: string;
12
+ packagePath: string;
13
+ }) =>
14
+ (j: core.JSCodeshift, source: Collection<Node>) => {
15
+ source
16
+ .find(j.JSXElement)
17
+ // @ts-ignore
18
+ .filter((path) => path.node.openingElement.name.name === componentName)
19
+ .replaceWith((element) => {
20
+ const elementName = j.jsxIdentifier(newComponentName);
21
+ const newComponent = j.jsxElement(
22
+ j.jsxOpeningElement(
23
+ elementName,
24
+ element.node.openingElement.attributes,
25
+ ),
26
+ j.jsxClosingElement(elementName),
27
+ element.node.children,
28
+ );
27
29
 
28
- return newComponent;
29
- });
30
+ return newComponent;
31
+ });
30
32
 
31
- return source.toSource();
32
- };
33
+ return source.toSource();
34
+ };
@@ -2,6 +2,8 @@
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
4
 
5
+ var _typeof = require("@babel/runtime/helpers/typeof");
6
+
5
7
  Object.defineProperty(exports, "__esModule", {
6
8
  value: true
7
9
  });
@@ -15,9 +17,7 @@ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/h
15
17
 
16
18
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
17
19
 
18
- var _react = require("react");
19
-
20
- var _react2 = require("@emotion/react");
20
+ var _react = _interopRequireWildcard(require("react"));
21
21
 
22
22
  var _bindEventListener = require("bind-event-listener");
23
23
 
@@ -47,6 +47,10 @@ var _useGeneratedId = _interopRequireDefault(require("./internal/utils/use-gener
47
47
 
48
48
  var _excluded = ["ref"];
49
49
 
50
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
51
+
52
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
53
+
50
54
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
51
55
 
52
56
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
@@ -189,7 +193,7 @@ var DropdownMenu = function DropdownMenu(props) {
189
193
  });
190
194
  }, [isFocused, isLocalOpen, handleTriggerClicked]);
191
195
  var id = (0, _useGeneratedId.default)();
192
- return (0, _react2.jsx)(_selectionStore.default, null, (0, _react2.jsx)(_popup.default, {
196
+ return /*#__PURE__*/_react.default.createElement(_selectionStore.default, null, /*#__PURE__*/_react.default.createElement(_popup.default, {
193
197
  id: isLocalOpen ? id : undefined,
194
198
  shouldFlip: shouldFlip,
195
199
  isOpen: isLocalOpen,
@@ -211,13 +215,13 @@ var DropdownMenu = function DropdownMenu(props) {
211
215
  }));
212
216
  }
213
217
 
214
- return (0, _react2.jsx)(_standardButton.default, (0, _extends2.default)({}, bindFocus, {
218
+ return /*#__PURE__*/_react.default.createElement(_standardButton.default, (0, _extends2.default)({}, bindFocus, {
215
219
  ref: triggerProps.ref,
216
220
  "aria-controls": triggerProps['aria-controls'],
217
221
  "aria-expanded": triggerProps['aria-expanded'],
218
222
  "aria-haspopup": triggerProps['aria-haspopup'],
219
223
  isSelected: isLocalOpen,
220
- iconAfter: (0, _react2.jsx)(_chevronDown.default, {
224
+ iconAfter: /*#__PURE__*/_react.default.createElement(_chevronDown.default, {
221
225
  size: "medium",
222
226
  label: ""
223
227
  }),
@@ -228,7 +232,7 @@ var DropdownMenu = function DropdownMenu(props) {
228
232
  content: function content(_ref) {
229
233
  var setInitialFocusRef = _ref.setInitialFocusRef,
230
234
  update = _ref.update;
231
- return (0, _react2.jsx)(_focusManager.default, null, (0, _react2.jsx)(_menuWrapper.default, {
235
+ return /*#__PURE__*/_react.default.createElement(_focusManager.default, null, /*#__PURE__*/_react.default.createElement(_menuWrapper.default, {
232
236
  maxHeight: MAX_HEIGHT,
233
237
  maxWidth: 800,
234
238
  onClose: handleOnClose,
@@ -19,8 +19,6 @@ var _menuGroup = _interopRequireDefault(require("@atlaskit/menu/menu-group"));
19
19
 
20
20
  var _spinner = _interopRequireDefault(require("@atlaskit/spinner"));
21
21
 
22
- var _constants = require("@atlaskit/theme/constants");
23
-
24
22
  var _visuallyHidden = _interopRequireDefault(require("@atlaskit/visually-hidden"));
25
23
 
26
24
  var _focusManager = require("../components/focus-manager");
@@ -30,24 +28,25 @@ var _isCheckboxItem = _interopRequireDefault(require("../utils/is-checkbox-item"
30
28
  var _isRadioItem = _interopRequireDefault(require("../utils/is-radio-item"));
31
29
 
32
30
  var _excluded = ["onClose", "onUpdate", "isLoading", "statusLabel", "setInitialFocusRef", "children"];
33
- var gridSize = (0, _constants.gridSize)();
34
31
  var spinnerContainerStyles = (0, _react2.css)({
35
32
  display: 'flex',
36
- minWidth: "".concat(gridSize * 20, "px"),
37
- padding: "".concat(gridSize * 2.5, "px"),
33
+ minWidth: '160px',
34
+ padding: "var(--ds-scale-250, 20px)",
38
35
  justifyContent: 'center'
39
36
  });
40
37
 
41
38
  var LoadingIndicator = function LoadingIndicator(_ref) {
42
39
  var _ref$statusLabel = _ref.statusLabel,
43
40
  statusLabel = _ref$statusLabel === void 0 ? 'Loading' : _ref$statusLabel;
44
- return (0, _react2.jsx)("div", {
45
- css: spinnerContainerStyles
46
- }, (0, _react2.jsx)(_spinner.default, {
47
- size: "small"
48
- }), (0, _react2.jsx)(_visuallyHidden.default, {
49
- role: "status"
50
- }, statusLabel));
41
+ return (// eslint-disable-next-line @repo/internal/react/use-primitives
42
+ (0, _react2.jsx)("div", {
43
+ css: spinnerContainerStyles
44
+ }, (0, _react2.jsx)(_spinner.default, {
45
+ size: "small"
46
+ }), (0, _react2.jsx)(_visuallyHidden.default, {
47
+ role: "status"
48
+ }, statusLabel))
49
+ );
51
50
  };
52
51
  /**
53
52
  *
@@ -75,7 +74,7 @@ var MenuWrapper = function MenuWrapper(_ref2) {
75
74
  var isCheckboxOrRadio = (0, _isCheckboxItem.default)(menuItem) || (0, _isRadioItem.default)(menuItem);
76
75
  return menuItem.contains(e.target) && !isCheckboxOrRadio;
77
76
  }); // Close menu if the click is triggered from a MenuItem or
78
- // its decendant. Don't close the menu if the click is triggered
77
+ // its descendant. Don't close the menu if the click is triggered
79
78
  // from a MenuItemRadio or MenuItemCheckbox so that the user can
80
79
  // select multiple items.
81
80
 
@@ -14,6 +14,43 @@ var _keycodes = require("@atlaskit/ds-lib/keycodes");
14
14
  var _actionMap;
15
15
 
16
16
  var actionMap = (_actionMap = {}, (0, _defineProperty2.default)(_actionMap, _keycodes.KEY_DOWN, 'next'), (0, _defineProperty2.default)(_actionMap, _keycodes.KEY_UP, 'prev'), (0, _defineProperty2.default)(_actionMap, _keycodes.KEY_HOME, 'first'), (0, _defineProperty2.default)(_actionMap, _keycodes.KEY_END, 'last'), _actionMap);
17
+ /**
18
+ * currentFocusedIdx + 1 will not work if the next focusable element
19
+ * is disabled. So, we need to iterate through the following menu items
20
+ * to find one that isn't disabled. If all following elements are disabled,
21
+ * return undefined.
22
+ */
23
+
24
+ var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
25
+ while (currentFocusedIdx + 1 < refs.length) {
26
+ var isDisabled = refs[currentFocusedIdx + 1].getAttribute('disabled') !== null;
27
+
28
+ if (!isDisabled) {
29
+ return refs[currentFocusedIdx + 1];
30
+ }
31
+
32
+ currentFocusedIdx++;
33
+ }
34
+ };
35
+ /**
36
+ * currentFocusedIdx - 1 will not work if the prev focusable element
37
+ * is disabled. So, we need to iterate through the previous menu items
38
+ * to find one that isn't disabled. If all previous elements are disabled,
39
+ * return undefined.
40
+ */
41
+
42
+
43
+ var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
44
+ while (currentFocusedIdx > 0) {
45
+ var isDisabled = refs[currentFocusedIdx - 1].getAttribute('disabled') !== null;
46
+
47
+ if (!isDisabled) {
48
+ return refs[currentFocusedIdx - 1];
49
+ }
50
+
51
+ currentFocusedIdx--;
52
+ }
53
+ };
17
54
 
18
55
  function handleFocus(refs) {
19
56
  return function (e) {
@@ -28,7 +65,10 @@ function handleFocus(refs) {
28
65
  case 'next':
29
66
  if (currentFocusedIdx < refs.length - 1) {
30
67
  e.preventDefault();
31
- refs[currentFocusedIdx + 1].focus();
68
+
69
+ var _nextFocusableElement = getNextFocusableElement(refs, currentFocusedIdx);
70
+
71
+ _nextFocusableElement && _nextFocusableElement.focus();
32
72
  }
33
73
 
34
74
  break;
@@ -36,19 +76,26 @@ function handleFocus(refs) {
36
76
  case 'prev':
37
77
  if (currentFocusedIdx > 0) {
38
78
  e.preventDefault();
39
- refs[currentFocusedIdx - 1].focus();
79
+
80
+ var _prevFocusableElement = getPrevFocusableElement(refs, currentFocusedIdx);
81
+
82
+ _prevFocusableElement && _prevFocusableElement.focus();
40
83
  }
41
84
 
42
85
  break;
43
86
 
44
87
  case 'first':
45
- e.preventDefault();
46
- refs[0].focus();
88
+ e.preventDefault(); // Search for first non-disabled element if first element is disabled
89
+
90
+ var nextFocusableElement = getNextFocusableElement(refs, -1);
91
+ nextFocusableElement && nextFocusableElement.focus();
47
92
  break;
48
93
 
49
94
  case 'last':
50
- e.preventDefault();
51
- refs[refs.length - 1].focus();
95
+ e.preventDefault(); // Search for last non-disabled element if last element is disabled
96
+
97
+ var prevFocusableElement = getPrevFocusableElement(refs, refs.length);
98
+ prevFocusableElement && prevFocusableElement.focus();
52
99
  break;
53
100
 
54
101
  default:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/dropdown-menu",
3
- "version": "11.5.4",
3
+ "version": "11.5.6",
4
4
  "sideEffects": false
5
5
  }
@@ -1,8 +1,5 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
-
3
- /** @jsx jsx */
4
- import { useCallback, useEffect, useMemo, useState } from 'react';
5
- import { jsx } from '@emotion/react';
2
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
3
  import { bind } from 'bind-event-listener';
7
4
  import Button from '@atlaskit/button/standard-button';
8
5
  import { KEY_DOWN } from '@atlaskit/ds-lib/keycodes';
@@ -137,7 +134,7 @@ const DropdownMenu = props => {
137
134
  });
138
135
  }, [isFocused, isLocalOpen, handleTriggerClicked]);
139
136
  const id = useGeneratedId();
140
- return jsx(SelectionStore, null, jsx(Popup, {
137
+ return /*#__PURE__*/React.createElement(SelectionStore, null, /*#__PURE__*/React.createElement(Popup, {
141
138
  id: isLocalOpen ? id : undefined,
142
139
  shouldFlip: shouldFlip,
143
140
  isOpen: isLocalOpen,
@@ -162,13 +159,13 @@ const DropdownMenu = props => {
162
159
  });
163
160
  }
164
161
 
165
- return jsx(Button, _extends({}, bindFocus, {
162
+ return /*#__PURE__*/React.createElement(Button, _extends({}, bindFocus, {
166
163
  ref: triggerProps.ref,
167
164
  "aria-controls": triggerProps['aria-controls'],
168
165
  "aria-expanded": triggerProps['aria-expanded'],
169
166
  "aria-haspopup": triggerProps['aria-haspopup'],
170
167
  isSelected: isLocalOpen,
171
- iconAfter: jsx(ExpandIcon, {
168
+ iconAfter: /*#__PURE__*/React.createElement(ExpandIcon, {
172
169
  size: "medium",
173
170
  label: ""
174
171
  }),
@@ -179,7 +176,7 @@ const DropdownMenu = props => {
179
176
  content: ({
180
177
  setInitialFocusRef,
181
178
  update
182
- }) => jsx(FocusManager, null, jsx(MenuWrapper, {
179
+ }) => /*#__PURE__*/React.createElement(FocusManager, null, /*#__PURE__*/React.createElement(MenuWrapper, {
183
180
  maxHeight: MAX_HEIGHT,
184
181
  maxWidth: 800,
185
182
  onClose: handleOnClose,
@@ -5,22 +5,21 @@ import { useContext, useLayoutEffect } from 'react';
5
5
  import { css, jsx } from '@emotion/react';
6
6
  import MenuGroup from '@atlaskit/menu/menu-group';
7
7
  import Spinner from '@atlaskit/spinner';
8
- import { gridSize as gridSizeFn } from '@atlaskit/theme/constants';
9
8
  import VisuallyHidden from '@atlaskit/visually-hidden';
10
9
  import { FocusManagerContext } from '../components/focus-manager';
11
10
  import isCheckboxItem from '../utils/is-checkbox-item';
12
11
  import isRadioItem from '../utils/is-radio-item';
13
- const gridSize = gridSizeFn();
14
12
  const spinnerContainerStyles = css({
15
13
  display: 'flex',
16
- minWidth: `${gridSize * 20}px`,
17
- padding: `${gridSize * 2.5}px`,
14
+ minWidth: '160px',
15
+ padding: "var(--ds-scale-250, 20px)",
18
16
  justifyContent: 'center'
19
17
  });
20
18
 
21
19
  const LoadingIndicator = ({
22
20
  statusLabel = 'Loading'
23
- }) => jsx("div", {
21
+ }) => // eslint-disable-next-line @repo/internal/react/use-primitives
22
+ jsx("div", {
24
23
  css: spinnerContainerStyles
25
24
  }, jsx(Spinner, {
26
25
  size: "small"
@@ -54,7 +53,7 @@ const MenuWrapper = ({
54
53
  const isCheckboxOrRadio = isCheckboxItem(menuItem) || isRadioItem(menuItem);
55
54
  return menuItem.contains(e.target) && !isCheckboxOrRadio;
56
55
  }); // Close menu if the click is triggered from a MenuItem or
57
- // its decendant. Don't close the menu if the click is triggered
56
+ // its descendant. Don't close the menu if the click is triggered
58
57
  // from a MenuItemRadio or MenuItemCheckbox so that the user can
59
58
  // select multiple items.
60
59
 
@@ -5,6 +5,44 @@ const actionMap = {
5
5
  [KEY_HOME]: 'first',
6
6
  [KEY_END]: 'last'
7
7
  };
8
+ /**
9
+ * currentFocusedIdx + 1 will not work if the next focusable element
10
+ * is disabled. So, we need to iterate through the following menu items
11
+ * to find one that isn't disabled. If all following elements are disabled,
12
+ * return undefined.
13
+ */
14
+
15
+ const getNextFocusableElement = (refs, currentFocusedIdx) => {
16
+ while (currentFocusedIdx + 1 < refs.length) {
17
+ const isDisabled = refs[currentFocusedIdx + 1].getAttribute('disabled') !== null;
18
+
19
+ if (!isDisabled) {
20
+ return refs[currentFocusedIdx + 1];
21
+ }
22
+
23
+ currentFocusedIdx++;
24
+ }
25
+ };
26
+ /**
27
+ * currentFocusedIdx - 1 will not work if the prev focusable element
28
+ * is disabled. So, we need to iterate through the previous menu items
29
+ * to find one that isn't disabled. If all previous elements are disabled,
30
+ * return undefined.
31
+ */
32
+
33
+
34
+ const getPrevFocusableElement = (refs, currentFocusedIdx) => {
35
+ while (currentFocusedIdx > 0) {
36
+ const isDisabled = refs[currentFocusedIdx - 1].getAttribute('disabled') !== null;
37
+
38
+ if (!isDisabled) {
39
+ return refs[currentFocusedIdx - 1];
40
+ }
41
+
42
+ currentFocusedIdx--;
43
+ }
44
+ };
45
+
8
46
  export default function handleFocus(refs) {
9
47
  return e => {
10
48
  const currentFocusedIdx = refs.findIndex(el => {
@@ -18,7 +56,8 @@ export default function handleFocus(refs) {
18
56
  case 'next':
19
57
  if (currentFocusedIdx < refs.length - 1) {
20
58
  e.preventDefault();
21
- refs[currentFocusedIdx + 1].focus();
59
+ const nextFocusableElement = getNextFocusableElement(refs, currentFocusedIdx);
60
+ nextFocusableElement && nextFocusableElement.focus();
22
61
  }
23
62
 
24
63
  break;
@@ -26,19 +65,24 @@ export default function handleFocus(refs) {
26
65
  case 'prev':
27
66
  if (currentFocusedIdx > 0) {
28
67
  e.preventDefault();
29
- refs[currentFocusedIdx - 1].focus();
68
+ const prevFocusableElement = getPrevFocusableElement(refs, currentFocusedIdx);
69
+ prevFocusableElement && prevFocusableElement.focus();
30
70
  }
31
71
 
32
72
  break;
33
73
 
34
74
  case 'first':
35
- e.preventDefault();
36
- refs[0].focus();
75
+ e.preventDefault(); // Search for first non-disabled element if first element is disabled
76
+
77
+ const nextFocusableElement = getNextFocusableElement(refs, -1);
78
+ nextFocusableElement && nextFocusableElement.focus();
37
79
  break;
38
80
 
39
81
  case 'last':
40
- e.preventDefault();
41
- refs[refs.length - 1].focus();
82
+ e.preventDefault(); // Search for last non-disabled element if last element is disabled
83
+
84
+ const prevFocusableElement = getPrevFocusableElement(refs, refs.length);
85
+ prevFocusableElement && prevFocusableElement.focus();
42
86
  break;
43
87
 
44
88
  default:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/dropdown-menu",
3
- "version": "11.5.4",
3
+ "version": "11.5.6",
4
4
  "sideEffects": false
5
5
  }
@@ -8,9 +8,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
8
8
 
9
9
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
10
10
 
11
- /** @jsx jsx */
12
- import { useCallback, useEffect, useMemo, useState } from 'react';
13
- import { jsx } from '@emotion/react';
11
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
14
12
  import { bind } from 'bind-event-listener';
15
13
  import Button from '@atlaskit/button/standard-button';
16
14
  import { KEY_DOWN } from '@atlaskit/ds-lib/keycodes';
@@ -162,7 +160,7 @@ var DropdownMenu = function DropdownMenu(props) {
162
160
  });
163
161
  }, [isFocused, isLocalOpen, handleTriggerClicked]);
164
162
  var id = useGeneratedId();
165
- return jsx(SelectionStore, null, jsx(Popup, {
163
+ return /*#__PURE__*/React.createElement(SelectionStore, null, /*#__PURE__*/React.createElement(Popup, {
166
164
  id: isLocalOpen ? id : undefined,
167
165
  shouldFlip: shouldFlip,
168
166
  isOpen: isLocalOpen,
@@ -185,13 +183,13 @@ var DropdownMenu = function DropdownMenu(props) {
185
183
  }));
186
184
  }
187
185
 
188
- return jsx(Button, _extends({}, bindFocus, {
186
+ return /*#__PURE__*/React.createElement(Button, _extends({}, bindFocus, {
189
187
  ref: triggerProps.ref,
190
188
  "aria-controls": triggerProps['aria-controls'],
191
189
  "aria-expanded": triggerProps['aria-expanded'],
192
190
  "aria-haspopup": triggerProps['aria-haspopup'],
193
191
  isSelected: isLocalOpen,
194
- iconAfter: jsx(ExpandIcon, {
192
+ iconAfter: /*#__PURE__*/React.createElement(ExpandIcon, {
195
193
  size: "medium",
196
194
  label: ""
197
195
  }),
@@ -202,7 +200,7 @@ var DropdownMenu = function DropdownMenu(props) {
202
200
  content: function content(_ref) {
203
201
  var setInitialFocusRef = _ref.setInitialFocusRef,
204
202
  update = _ref.update;
205
- return jsx(FocusManager, null, jsx(MenuWrapper, {
203
+ return /*#__PURE__*/React.createElement(FocusManager, null, /*#__PURE__*/React.createElement(MenuWrapper, {
206
204
  maxHeight: MAX_HEIGHT,
207
205
  maxWidth: 800,
208
206
  onClose: handleOnClose,
@@ -7,29 +7,29 @@ import { useContext, useLayoutEffect } from 'react';
7
7
  import { css, jsx } from '@emotion/react';
8
8
  import MenuGroup from '@atlaskit/menu/menu-group';
9
9
  import Spinner from '@atlaskit/spinner';
10
- import { gridSize as gridSizeFn } from '@atlaskit/theme/constants';
11
10
  import VisuallyHidden from '@atlaskit/visually-hidden';
12
11
  import { FocusManagerContext } from '../components/focus-manager';
13
12
  import isCheckboxItem from '../utils/is-checkbox-item';
14
13
  import isRadioItem from '../utils/is-radio-item';
15
- var gridSize = gridSizeFn();
16
14
  var spinnerContainerStyles = css({
17
15
  display: 'flex',
18
- minWidth: "".concat(gridSize * 20, "px"),
19
- padding: "".concat(gridSize * 2.5, "px"),
16
+ minWidth: '160px',
17
+ padding: "var(--ds-scale-250, 20px)",
20
18
  justifyContent: 'center'
21
19
  });
22
20
 
23
21
  var LoadingIndicator = function LoadingIndicator(_ref) {
24
22
  var _ref$statusLabel = _ref.statusLabel,
25
23
  statusLabel = _ref$statusLabel === void 0 ? 'Loading' : _ref$statusLabel;
26
- return jsx("div", {
27
- css: spinnerContainerStyles
28
- }, jsx(Spinner, {
29
- size: "small"
30
- }), jsx(VisuallyHidden, {
31
- role: "status"
32
- }, statusLabel));
24
+ return (// eslint-disable-next-line @repo/internal/react/use-primitives
25
+ jsx("div", {
26
+ css: spinnerContainerStyles
27
+ }, jsx(Spinner, {
28
+ size: "small"
29
+ }), jsx(VisuallyHidden, {
30
+ role: "status"
31
+ }, statusLabel))
32
+ );
33
33
  };
34
34
  /**
35
35
  *
@@ -57,7 +57,7 @@ var MenuWrapper = function MenuWrapper(_ref2) {
57
57
  var isCheckboxOrRadio = isCheckboxItem(menuItem) || isRadioItem(menuItem);
58
58
  return menuItem.contains(e.target) && !isCheckboxOrRadio;
59
59
  }); // Close menu if the click is triggered from a MenuItem or
60
- // its decendant. Don't close the menu if the click is triggered
60
+ // its descendant. Don't close the menu if the click is triggered
61
61
  // from a MenuItemRadio or MenuItemCheckbox so that the user can
62
62
  // select multiple items.
63
63
 
@@ -4,6 +4,44 @@ var _actionMap;
4
4
 
5
5
  import { KEY_DOWN, KEY_END, KEY_HOME, KEY_UP } from '@atlaskit/ds-lib/keycodes';
6
6
  var actionMap = (_actionMap = {}, _defineProperty(_actionMap, KEY_DOWN, 'next'), _defineProperty(_actionMap, KEY_UP, 'prev'), _defineProperty(_actionMap, KEY_HOME, 'first'), _defineProperty(_actionMap, KEY_END, 'last'), _actionMap);
7
+ /**
8
+ * currentFocusedIdx + 1 will not work if the next focusable element
9
+ * is disabled. So, we need to iterate through the following menu items
10
+ * to find one that isn't disabled. If all following elements are disabled,
11
+ * return undefined.
12
+ */
13
+
14
+ var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
15
+ while (currentFocusedIdx + 1 < refs.length) {
16
+ var isDisabled = refs[currentFocusedIdx + 1].getAttribute('disabled') !== null;
17
+
18
+ if (!isDisabled) {
19
+ return refs[currentFocusedIdx + 1];
20
+ }
21
+
22
+ currentFocusedIdx++;
23
+ }
24
+ };
25
+ /**
26
+ * currentFocusedIdx - 1 will not work if the prev focusable element
27
+ * is disabled. So, we need to iterate through the previous menu items
28
+ * to find one that isn't disabled. If all previous elements are disabled,
29
+ * return undefined.
30
+ */
31
+
32
+
33
+ var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
34
+ while (currentFocusedIdx > 0) {
35
+ var isDisabled = refs[currentFocusedIdx - 1].getAttribute('disabled') !== null;
36
+
37
+ if (!isDisabled) {
38
+ return refs[currentFocusedIdx - 1];
39
+ }
40
+
41
+ currentFocusedIdx--;
42
+ }
43
+ };
44
+
7
45
  export default function handleFocus(refs) {
8
46
  return function (e) {
9
47
  var currentFocusedIdx = refs.findIndex(function (el) {
@@ -17,7 +55,10 @@ export default function handleFocus(refs) {
17
55
  case 'next':
18
56
  if (currentFocusedIdx < refs.length - 1) {
19
57
  e.preventDefault();
20
- refs[currentFocusedIdx + 1].focus();
58
+
59
+ var _nextFocusableElement = getNextFocusableElement(refs, currentFocusedIdx);
60
+
61
+ _nextFocusableElement && _nextFocusableElement.focus();
21
62
  }
22
63
 
23
64
  break;
@@ -25,19 +66,26 @@ export default function handleFocus(refs) {
25
66
  case 'prev':
26
67
  if (currentFocusedIdx > 0) {
27
68
  e.preventDefault();
28
- refs[currentFocusedIdx - 1].focus();
69
+
70
+ var _prevFocusableElement = getPrevFocusableElement(refs, currentFocusedIdx);
71
+
72
+ _prevFocusableElement && _prevFocusableElement.focus();
29
73
  }
30
74
 
31
75
  break;
32
76
 
33
77
  case 'first':
34
- e.preventDefault();
35
- refs[0].focus();
78
+ e.preventDefault(); // Search for first non-disabled element if first element is disabled
79
+
80
+ var nextFocusableElement = getNextFocusableElement(refs, -1);
81
+ nextFocusableElement && nextFocusableElement.focus();
36
82
  break;
37
83
 
38
84
  case 'last':
39
- e.preventDefault();
40
- refs[refs.length - 1].focus();
85
+ e.preventDefault(); // Search for last non-disabled element if last element is disabled
86
+
87
+ var prevFocusableElement = getPrevFocusableElement(refs, refs.length);
88
+ prevFocusableElement && prevFocusableElement.focus();
41
89
  break;
42
90
 
43
91
  default:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/dropdown-menu",
3
- "version": "11.5.4",
3
+ "version": "11.5.6",
4
4
  "sideEffects": false
5
5
  }
@@ -1,4 +1,4 @@
1
- import { jsx } from '@emotion/react';
1
+ /// <reference types="react" />
2
2
  import type { DropdownMenuProps } from './types';
3
3
  /**
4
4
  * __Dropdown menu__
@@ -9,5 +9,5 @@ import type { DropdownMenuProps } from './types';
9
9
  * - [Code](https://atlassian.design/components/dropdown-menu/code)
10
10
  * - [Usage](https://atlassian.design/components/dropdown-menu/usage)
11
11
  */
12
- declare const DropdownMenu: <T extends HTMLElement = HTMLElement>(props: DropdownMenuProps<T>) => jsx.JSX.Element;
12
+ declare const DropdownMenu: <T extends HTMLElement = HTMLElement>(props: DropdownMenuProps<T>) => JSX.Element;
13
13
  export default DropdownMenu;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/dropdown-menu",
3
- "version": "11.5.4",
3
+ "version": "11.5.6",
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/"
@@ -32,7 +32,7 @@
32
32
  "@atlaskit/popup": "^1.5.0",
33
33
  "@atlaskit/spinner": "^15.2.0",
34
34
  "@atlaskit/theme": "^12.2.0",
35
- "@atlaskit/tokens": "^0.10.0",
35
+ "@atlaskit/tokens": "^0.11.0",
36
36
  "@atlaskit/visually-hidden": "^1.1.0",
37
37
  "@babel/runtime": "^7.0.0",
38
38
  "@emotion/react": "^11.7.1",
@@ -45,9 +45,9 @@
45
45
  "devDependencies": {
46
46
  "@atlaskit/avatar": "^21.1.0",
47
47
  "@atlaskit/docs": "*",
48
- "@atlaskit/ds-explorations": "^1.0.0",
49
- "@atlaskit/heading": "^0.1.18",
50
- "@atlaskit/lozenge": "11.3.0",
48
+ "@atlaskit/ds-explorations": "^1.4.0",
49
+ "@atlaskit/heading": "^1.0.0",
50
+ "@atlaskit/lozenge": "11.3.1",
51
51
  "@atlaskit/modal-dialog": "^12.4.0",
52
52
  "@atlaskit/section-message": "^6.3.0",
53
53
  "@atlaskit/ssr": "*",
@@ -76,9 +76,14 @@
76
76
  },
77
77
  "@repo/internal": {
78
78
  "dom-events": "use-bind-event-listener",
79
- "ui-components": "lite-mode",
79
+ "ui-components": [
80
+ "primitives",
81
+ "lite-mode"
82
+ ],
80
83
  "design-system": "v1",
81
- "design-tokens": "spacing",
84
+ "design-tokens": [
85
+ "spacing"
86
+ ],
82
87
  "styling": [
83
88
  "static",
84
89
  "emotion"
package/report.api.md CHANGED
@@ -25,7 +25,7 @@ import type { TriggerProps } from '@atlaskit/popup/types';
25
25
 
26
26
  // @public (undocumented)
27
27
  export interface CustomTriggerProps<
28
- TriggerElement extends HTMLElement = HTMLElement
28
+ TriggerElement extends HTMLElement = HTMLElement,
29
29
  > extends Omit<TriggerProps, 'ref'> {
30
30
  isSelected?: boolean;
31
31
  onClick?: (e: MouseEvent_2 | KeyboardEvent_2) => void;
@@ -127,7 +127,7 @@ export interface DropdownMenuGroupProps extends SectionProps_2 {}
127
127
 
128
128
  // @public (undocumented)
129
129
  export interface DropdownMenuProps<
130
- TriggerElement extends HTMLElement = HTMLElement
130
+ TriggerElement extends HTMLElement = HTMLElement,
131
131
  > {
132
132
  appearance?: 'default' | 'tall';
133
133
  autoFocus?: boolean;