@atlaskit/avatar-group 12.10.12 → 13.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @atlaskit/avatar-group
2
2
 
3
+ ## 13.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`f2dc9097319f0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f2dc9097319f0) - ###
8
+ Dropped support for _legacy_ Typescript 4 types. **Typescript 5 is now the new minimum**.
9
+
10
+ Removes the `typesVersions` property and `dist/types-ts4.5` directory from the dist.
11
+
12
+ Types are now exclusively via the `"types": "dist/types/index.d.ts"` property.
13
+
14
+ ```diff
15
+ - "typesVersions": {
16
+ - ">=4.5 <4.9": {
17
+ - "*": [
18
+ - "dist/types-ts4.5/*",
19
+ - "dist/types-ts4.5/index.d.ts"
20
+ - ]
21
+ - }
22
+ - },
23
+ ```
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+
29
+ ## 12.11.0
30
+
31
+ ### Minor Changes
32
+
33
+ - [`032135d12186b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/032135d12186b) -
34
+ Top-layer: fix initial focus regressions on the top-layer adapters for `@atlaskit/avatar-group`
35
+ and `@atlaskit/popup`. Behind the `platform-dst-top-layer` feature gate.
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies
40
+
3
41
  ## 12.10.12
4
42
 
5
43
  ### Patch Changes
@@ -0,0 +1,78 @@
1
+ import { expect, test } from '@af/integration-testing';
2
+
3
+ /**
4
+ * Avatar group: focus contract on the top-layer code path.
5
+ *
6
+ * `AvatarGroup` renders a "more" trigger that opens a `role="menu"` popover
7
+ * listing avatars beyond `maxCount`. The focus contract is:
8
+ *
9
+ * 1. Initial focus moves to the first item in the avatar list on open.
10
+ * 2. Escape closes the popover and restores focus to the more trigger.
11
+ * 3. ArrowDown moves focus between items within the menu.
12
+ *
13
+ * See: `platform/packages/design-system/top-layer/notes/architecture/focus.md`.
14
+ */
15
+
16
+ const featureFlag = 'platform-dst-top-layer';
17
+
18
+ test.describe('Avatar group: top-layer focus contract', () => {
19
+ test('initial focus: focus moves to the first overflow item on open', async ({ page }) => {
20
+ await page.visitExample<typeof import('../../examples/testing-top-layer-focus.tsx')>(
21
+ 'design-system',
22
+ 'avatar-group',
23
+ 'testing-top-layer-focus',
24
+ { featureFlag },
25
+ );
26
+
27
+ const moreTrigger = page.getByTestId('avatar-group--overflow-menu--trigger');
28
+ await moreTrigger.click();
29
+
30
+ const menu = page.getByRole('menu');
31
+ await expect(menu).toBeVisible();
32
+ // avatar-group computes `maxAvatar = total > max ? max - 1 : max` and
33
+ // emits overflow items with testId `${testId}--avatar-group-item-${index + maxAvatar}`.
34
+ // With `data.length=6` and `maxCount=3`, `maxAvatar=2`, so the first
35
+ // overflow item is at index 2.
36
+ const firstItem = page.getByTestId('avatar-group--avatar-group-item-2');
37
+ await expect(firstItem).toBeFocused();
38
+ });
39
+
40
+ test('focus restoration: Escape restores focus to the more trigger', async ({ page }) => {
41
+ await page.visitExample<typeof import('../../examples/testing-top-layer-focus.tsx')>(
42
+ 'design-system',
43
+ 'avatar-group',
44
+ 'testing-top-layer-focus',
45
+ { featureFlag },
46
+ );
47
+
48
+ const moreTrigger = page.getByTestId('avatar-group--overflow-menu--trigger');
49
+ await moreTrigger.focus();
50
+ await page.keyboard.press('ArrowDown');
51
+ await expect(page.getByRole('menu')).toBeVisible();
52
+
53
+ await page.keyboard.press('Escape');
54
+ await expect(moreTrigger).toBeFocused();
55
+ });
56
+
57
+ test('focus movement: ArrowDown moves focus between items within the menu', async ({ page }) => {
58
+ await page.visitExample<typeof import('../../examples/testing-top-layer-focus.tsx')>(
59
+ 'design-system',
60
+ 'avatar-group',
61
+ 'testing-top-layer-focus',
62
+ { featureFlag },
63
+ );
64
+
65
+ const moreTrigger = page.getByTestId('avatar-group--overflow-menu--trigger');
66
+ await moreTrigger.focus();
67
+ await page.keyboard.press('ArrowDown');
68
+
69
+ const menu = page.getByRole('menu');
70
+ await expect(menu).toBeVisible();
71
+
72
+ const items = menu.getByRole('menuitem');
73
+ await expect(items.first()).toBeFocused();
74
+
75
+ await page.keyboard.press('ArrowDown');
76
+ await expect(items.nth(1)).toBeFocused();
77
+ });
78
+ });
@@ -11,7 +11,6 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
11
11
  var _react = _interopRequireWildcard(require("react"));
12
12
  var _bindEventListener = require("bind-event-listener");
13
13
  var _keycodes = require("@atlaskit/ds-lib/keycodes");
14
- var _useFocusEvent = _interopRequireDefault(require("@atlaskit/ds-lib/use-focus-event"));
15
14
  var _menu = require("@atlaskit/menu");
16
15
  var _animations = require("@atlaskit/top-layer/animations");
17
16
  var _getAriaForTrigger = require("@atlaskit/top-layer/get-aria-for-trigger");
@@ -107,27 +106,27 @@ function MoreDropdownTopLayer(_ref) {
107
106
 
108
107
  // ArrowDown-to-open: when the trigger is focused and the menu is closed,
109
108
  // pressing ArrowDown opens the menu (WAI-ARIA menu button pattern).
110
- // We track focus on the trigger wrapper to avoid threading onFocus/onBlur
111
- // through the renderMoreButton/MoreIndicator prop plumbing.
112
- var triggerWrapperRef = (0, _react.useRef)(null);
113
- var _useFocus = (0, _useFocusEvent.default)(),
114
- isFocused = _useFocus.isFocused,
115
- triggerFocusBind = _useFocus.bindFocus;
109
+ //
110
+ // Bind the keydown listener directly to the trigger element via its ref so
111
+ // that opening the menu does not trigger a focus-driven re-render of the
112
+ // host component, which would unmount and remount the menu items and drop
113
+ // menuitem focus.
116
114
  (0, _react.useEffect)(function () {
117
- if (!isFocused || isOpen) {
115
+ var trigger = triggerRef.current;
116
+ if (!trigger || isOpen) {
118
117
  return;
119
118
  }
120
- return (0, _bindEventListener.bind)(window, {
119
+ return (0, _bindEventListener.bind)(trigger, {
121
120
  type: 'keydown',
122
- listener: function openOnArrowDown(e) {
123
- if (e.key === _keycodes.KEY_DOWN) {
121
+ listener: function openOnArrowDown(event) {
122
+ if (event.key === _keycodes.KEY_DOWN) {
124
123
  // Prevent page scroll when opening the menu via ArrowDown.
125
- e.preventDefault();
126
- handleTriggerClicked(e);
124
+ event.preventDefault();
125
+ handleTriggerClicked(event);
127
126
  }
128
127
  }
129
128
  });
130
- }, [isFocused, isOpen, handleTriggerClicked]);
129
+ }, [isOpen, handleTriggerClicked]);
131
130
  var triggerAria = (0, _getAriaForTrigger.getAriaForTrigger)({
132
131
  role: 'menu',
133
132
  isOpen: isOpen,
@@ -136,20 +135,11 @@ function MoreDropdownTopLayer(_ref) {
136
135
  var setTriggerRef = (0, _react.useCallback)(function (node) {
137
136
  triggerRef.current = node;
138
137
  }, []);
139
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", {
140
- ref: triggerWrapperRef,
141
- onFocus: triggerFocusBind.onFocus,
142
- onBlur: triggerFocusBind.onBlur
143
- // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- display: contents is a layout-neutral wrapper; it cannot affect visual output.
144
- ,
145
- style: {
146
- display: 'contents'
147
- }
148
- }, renderMoreButton(_objectSpread(_objectSpread({
138
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, renderMoreButton(_objectSpread(_objectSpread({
149
139
  ref: setTriggerRef
150
140
  }, triggerAria), {}, {
151
141
  onClick: handleTriggerClicked
152
- }))), /*#__PURE__*/_react.default.createElement(_popover.Popover, {
142
+ })), /*#__PURE__*/_react.default.createElement(_popover.Popover, {
153
143
  ref: popoverRef,
154
144
  id: popoverId,
155
145
  role: "menu",
@@ -2,7 +2,6 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
  import React, { useCallback, useEffect, useRef } from 'react';
3
3
  import { bind } from 'bind-event-listener';
4
4
  import { KEY_DOWN } from '@atlaskit/ds-lib/keycodes';
5
- import useFocus from '@atlaskit/ds-lib/use-focus-event';
6
5
  import { MenuGroup, Section } from '@atlaskit/menu';
7
6
  import { slideAndFade } from '@atlaskit/top-layer/animations';
8
7
  import { getAriaForTrigger } from '@atlaskit/top-layer/get-aria-for-trigger';
@@ -94,28 +93,27 @@ export function MoreDropdownTopLayer({
94
93
 
95
94
  // ArrowDown-to-open: when the trigger is focused and the menu is closed,
96
95
  // pressing ArrowDown opens the menu (WAI-ARIA menu button pattern).
97
- // We track focus on the trigger wrapper to avoid threading onFocus/onBlur
98
- // through the renderMoreButton/MoreIndicator prop plumbing.
99
- const triggerWrapperRef = useRef(null);
100
- const {
101
- isFocused,
102
- bindFocus: triggerFocusBind
103
- } = useFocus();
96
+ //
97
+ // Bind the keydown listener directly to the trigger element via its ref so
98
+ // that opening the menu does not trigger a focus-driven re-render of the
99
+ // host component, which would unmount and remount the menu items and drop
100
+ // menuitem focus.
104
101
  useEffect(() => {
105
- if (!isFocused || isOpen) {
102
+ const trigger = triggerRef.current;
103
+ if (!trigger || isOpen) {
106
104
  return;
107
105
  }
108
- return bind(window, {
106
+ return bind(trigger, {
109
107
  type: 'keydown',
110
- listener: function openOnArrowDown(e) {
111
- if (e.key === KEY_DOWN) {
108
+ listener: function openOnArrowDown(event) {
109
+ if (event.key === KEY_DOWN) {
112
110
  // Prevent page scroll when opening the menu via ArrowDown.
113
- e.preventDefault();
114
- handleTriggerClicked(e);
111
+ event.preventDefault();
112
+ handleTriggerClicked(event);
115
113
  }
116
114
  }
117
115
  });
118
- }, [isFocused, isOpen, handleTriggerClicked]);
116
+ }, [isOpen, handleTriggerClicked]);
119
117
  const triggerAria = getAriaForTrigger({
120
118
  role: 'menu',
121
119
  isOpen,
@@ -124,20 +122,11 @@ export function MoreDropdownTopLayer({
124
122
  const setTriggerRef = useCallback(node => {
125
123
  triggerRef.current = node;
126
124
  }, []);
127
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
128
- ref: triggerWrapperRef,
129
- onFocus: triggerFocusBind.onFocus,
130
- onBlur: triggerFocusBind.onBlur
131
- // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- display: contents is a layout-neutral wrapper; it cannot affect visual output.
132
- ,
133
- style: {
134
- display: 'contents'
135
- }
136
- }, renderMoreButton({
125
+ return /*#__PURE__*/React.createElement(React.Fragment, null, renderMoreButton({
137
126
  ref: setTriggerRef,
138
127
  ...triggerAria,
139
128
  onClick: handleTriggerClicked
140
- })), /*#__PURE__*/React.createElement(Popover, {
129
+ }), /*#__PURE__*/React.createElement(Popover, {
141
130
  ref: popoverRef,
142
131
  id: popoverId,
143
132
  role: "menu",
@@ -5,7 +5,6 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
5
5
  import React, { useCallback, useEffect, useRef } from 'react';
6
6
  import { bind } from 'bind-event-listener';
7
7
  import { KEY_DOWN } from '@atlaskit/ds-lib/keycodes';
8
- import useFocus from '@atlaskit/ds-lib/use-focus-event';
9
8
  import { MenuGroup, Section } from '@atlaskit/menu';
10
9
  import { slideAndFade } from '@atlaskit/top-layer/animations';
11
10
  import { getAriaForTrigger } from '@atlaskit/top-layer/get-aria-for-trigger';
@@ -98,27 +97,27 @@ export function MoreDropdownTopLayer(_ref) {
98
97
 
99
98
  // ArrowDown-to-open: when the trigger is focused and the menu is closed,
100
99
  // pressing ArrowDown opens the menu (WAI-ARIA menu button pattern).
101
- // We track focus on the trigger wrapper to avoid threading onFocus/onBlur
102
- // through the renderMoreButton/MoreIndicator prop plumbing.
103
- var triggerWrapperRef = useRef(null);
104
- var _useFocus = useFocus(),
105
- isFocused = _useFocus.isFocused,
106
- triggerFocusBind = _useFocus.bindFocus;
100
+ //
101
+ // Bind the keydown listener directly to the trigger element via its ref so
102
+ // that opening the menu does not trigger a focus-driven re-render of the
103
+ // host component, which would unmount and remount the menu items and drop
104
+ // menuitem focus.
107
105
  useEffect(function () {
108
- if (!isFocused || isOpen) {
106
+ var trigger = triggerRef.current;
107
+ if (!trigger || isOpen) {
109
108
  return;
110
109
  }
111
- return bind(window, {
110
+ return bind(trigger, {
112
111
  type: 'keydown',
113
- listener: function openOnArrowDown(e) {
114
- if (e.key === KEY_DOWN) {
112
+ listener: function openOnArrowDown(event) {
113
+ if (event.key === KEY_DOWN) {
115
114
  // Prevent page scroll when opening the menu via ArrowDown.
116
- e.preventDefault();
117
- handleTriggerClicked(e);
115
+ event.preventDefault();
116
+ handleTriggerClicked(event);
118
117
  }
119
118
  }
120
119
  });
121
- }, [isFocused, isOpen, handleTriggerClicked]);
120
+ }, [isOpen, handleTriggerClicked]);
122
121
  var triggerAria = getAriaForTrigger({
123
122
  role: 'menu',
124
123
  isOpen: isOpen,
@@ -127,20 +126,11 @@ export function MoreDropdownTopLayer(_ref) {
127
126
  var setTriggerRef = useCallback(function (node) {
128
127
  triggerRef.current = node;
129
128
  }, []);
130
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
131
- ref: triggerWrapperRef,
132
- onFocus: triggerFocusBind.onFocus,
133
- onBlur: triggerFocusBind.onBlur
134
- // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- display: contents is a layout-neutral wrapper; it cannot affect visual output.
135
- ,
136
- style: {
137
- display: 'contents'
138
- }
139
- }, renderMoreButton(_objectSpread(_objectSpread({
129
+ return /*#__PURE__*/React.createElement(React.Fragment, null, renderMoreButton(_objectSpread(_objectSpread({
140
130
  ref: setTriggerRef
141
131
  }, triggerAria), {}, {
142
132
  onClick: handleTriggerClicked
143
- }))), /*#__PURE__*/React.createElement(Popover, {
133
+ })), /*#__PURE__*/React.createElement(Popover, {
144
134
  ref: popoverRef,
145
135
  id: popoverId,
146
136
  role: "menu",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/avatar-group",
3
- "version": "12.10.12",
3
+ "version": "13.0.0",
4
4
  "description": "An avatar group displays a number of avatars grouped together in a stack or grid.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -31,16 +31,16 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@atlaskit/avatar": "^25.15.0",
35
- "@atlaskit/css": "^0.19.0",
36
- "@atlaskit/ds-lib": "^7.0.0",
37
- "@atlaskit/menu": "^8.5.0",
38
- "@atlaskit/motion": "^6.2.0",
39
- "@atlaskit/platform-feature-flags": "^1.1.0",
40
- "@atlaskit/popup": "^4.25.0",
41
- "@atlaskit/tokens": "^13.4.0",
42
- "@atlaskit/tooltip": "^22.6.0",
43
- "@atlaskit/top-layer": "^0.18.0",
34
+ "@atlaskit/avatar": "^26.0.0",
35
+ "@atlaskit/css": "^1.0.0",
36
+ "@atlaskit/ds-lib": "^8.0.0",
37
+ "@atlaskit/menu": "^9.0.0",
38
+ "@atlaskit/motion": "^7.0.0",
39
+ "@atlaskit/platform-feature-flags": "^2.0.0",
40
+ "@atlaskit/popup": "^5.0.0",
41
+ "@atlaskit/tokens": "^14.0.0",
42
+ "@atlaskit/tooltip": "^23.0.0",
43
+ "@atlaskit/top-layer": "^1.0.0",
44
44
  "@babel/runtime": "^7.0.0",
45
45
  "@compiled/react": "^0.20.0",
46
46
  "bind-event-listener": "^3.0.0"
@@ -52,16 +52,16 @@
52
52
  "@af/accessibility-testing": "workspace:^",
53
53
  "@af/integration-testing": "workspace:^",
54
54
  "@af/visual-regression": "workspace:^",
55
- "@atlaskit/analytics-next": "^11.3.0",
56
- "@atlaskit/button": "^23.11.0",
57
- "@atlaskit/docs": "^11.8.0",
58
- "@atlaskit/form": "^15.5.0",
59
- "@atlaskit/icon": "^35.4.0",
60
- "@atlaskit/link": "^3.4.0",
61
- "@atlaskit/modal-dialog": "^15.3.0",
62
- "@atlaskit/primitives": "^19.0.0",
63
- "@atlaskit/section-message": "^8.13.0",
64
- "@atlaskit/toggle": "^16.1.0",
55
+ "@atlaskit/analytics-next": "^12.0.0",
56
+ "@atlaskit/button": "^24.0.0",
57
+ "@atlaskit/docs": "^12.0.0",
58
+ "@atlaskit/form": "^16.0.0",
59
+ "@atlaskit/icon": "^36.0.0",
60
+ "@atlaskit/link": "^4.0.0",
61
+ "@atlaskit/modal-dialog": "^16.0.0",
62
+ "@atlaskit/primitives": "^20.0.0",
63
+ "@atlaskit/section-message": "^9.0.0",
64
+ "@atlaskit/toggle": "^17.0.0",
65
65
  "@atlassian/feature-flags-test-utils": "^1.1.0",
66
66
  "@atlassian/react-compiler-gating": "workspace:^",
67
67
  "@atlassian/ssr-tests": "workspace:^",
@@ -98,14 +98,6 @@
98
98
  ]
99
99
  }
100
100
  },
101
- "typesVersions": {
102
- ">=4.5 <4.9": {
103
- "*": [
104
- "dist/types-ts4.5/*",
105
- "dist/types-ts4.5/index.d.ts"
106
- ]
107
- }
108
- },
109
101
  "homepage": "https://atlassian.design/components/avatar-group/",
110
102
  "platform-feature-flags": {
111
103
  "platform-avatar-group-spacing-fix": {