@atlaskit/popup 4.21.2 → 4.22.1

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,25 @@
1
1
  # @atlaskit/popup
2
2
 
3
+ ## 4.22.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ae5198c2f44ed`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ae5198c2f44ed) -
8
+ Fix @atlaskit/popup containing a portal-rendered child overlay (Drawer, Modal, etc.) now correctly
9
+ ignores clicks inside that overlay
10
+
11
+ ## 4.22.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [`cc76d2e733b71`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cc76d2e733b71) -
16
+ Updates the top layer popup trigger's `aria-expanded` attribute, so it stays `"true"` for the
17
+ duration of the popup's exit animation, only going `false` after the animation completes.
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 4.21.2
4
24
 
5
25
  ### Patch Changes
@@ -127,9 +127,6 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
127
127
  titleId: titleId
128
128
  });
129
129
 
130
- // Sync controlled isOpen with internal state
131
- var effectiveIsOpen = isOpen;
132
-
133
130
  // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
134
131
  // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
135
132
  var Container = PopupContainer;
@@ -150,8 +147,14 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
150
147
  _ref4(node);
151
148
  }
152
149
  },
153
- 'aria-controls': effectiveIsOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
154
- 'aria-expanded': effectiveIsOpen,
150
+ 'aria-controls': isOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
151
+ // Use ariaAttributes['aria-expanded'] (which comes from `@atlaskit/top-layer`), rather than
152
+ // `isOpen` directly. `@atlaskit/top-layer` tracks the full animation lifecycle via
153
+ // TPopupState and only goes false after the exit animation completes, so:
154
+ // - Screen readers don't announce the popup as closed while it is still visible.
155
+ // - Consumers relying on aria-expanded to show/hide trigger UI don't lose the
156
+ // trigger (popup anchor) before the animation finishes.
157
+ 'aria-expanded': ariaAttributes['aria-expanded'],
155
158
  // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
156
159
  // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
157
160
  // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
@@ -167,7 +170,7 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
167
170
  };
168
171
  return trigger(triggerProps);
169
172
  }), /*#__PURE__*/React.createElement(_popup.Popup.Content, (0, _extends2.default)({}, roleProps, {
170
- isOpen: effectiveIsOpen,
173
+ isOpen: isOpen,
171
174
  animate: animation,
172
175
  testId: testId && "".concat(testId, "--content"),
173
176
  widthFromAnchor: shouldFitContainer ? 'match-anchor' : 'none'
@@ -68,23 +68,45 @@ var useCloseManager = exports.useCloseManager = function useCloseManager(_ref) {
68
68
  if (!doesDomNodeExist) {
69
69
  return;
70
70
  }
71
- if (isLayerDisabled()) {
71
+ if ((0, _platformFeatureFlags.fg)('jsmgrowth_popup_fix')) {
72
72
  if (target instanceof HTMLElement) {
73
73
  var _target$closest;
74
74
  var layeredElement = (_target$closest = target.closest) === null || _target$closest === void 0 ? void 0 : _target$closest.call(target, "[data-ds--level]");
75
75
  if (layeredElement) {
76
- var closeType = layeredElement.getAttribute('[data-ds--close--type]');
77
- if (closeType === 'single') {
76
+ var closeType = layeredElement.getAttribute('data-ds--close--type');
77
+ if (closeType === 'single' && isLayerDisabled()) {
78
78
  // if the close type is single, we won't close other disabled layers when clicking outside
79
79
  return;
80
80
  }
81
81
  var levelOfClickedLayer = layeredElement.getAttribute('data-ds--level');
82
82
  if (levelOfClickedLayer && Number(levelOfClickedLayer) > currentLevel) {
83
- // won't trigger onClick event when we click in a higher layer.
83
+ // Don't close this popup when the click landed inside a higher-level layer
84
+ // (e.g. a Drawer or Modal that portals to <body>). Such nodes are not DOM
85
+ // descendants of popupRef, so the contains() checks below would wrongly
86
+ // classify them as "outside" clicks and close the popup prematurely.
84
87
  return;
85
88
  }
86
89
  }
87
90
  }
91
+ } else {
92
+ if (isLayerDisabled()) {
93
+ if (target instanceof HTMLElement) {
94
+ var _target$closest2;
95
+ var _layeredElement = (_target$closest2 = target.closest) === null || _target$closest2 === void 0 ? void 0 : _target$closest2.call(target, "[data-ds--level]");
96
+ if (_layeredElement) {
97
+ var _closeType = _layeredElement.getAttribute('[data-ds--close--type]');
98
+ if (_closeType === 'single') {
99
+ // if the close type is single, we won't close other disabled layers when clicking outside
100
+ return;
101
+ }
102
+ var _levelOfClickedLayer = _layeredElement.getAttribute('data-ds--level');
103
+ if (_levelOfClickedLayer && Number(_levelOfClickedLayer) > currentLevel) {
104
+ // won't trigger onClick event when we click in a higher layer.
105
+ return;
106
+ }
107
+ }
108
+ }
109
+ }
88
110
  }
89
111
  var isClickOnPopup = popupRef && popupRef.contains(target);
90
112
  var isClickOnTrigger = triggerRef && triggerRef.contains(target);
@@ -134,9 +134,6 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
134
134
  titleId
135
135
  });
136
136
 
137
- // Sync controlled isOpen with internal state
138
- const effectiveIsOpen = isOpen;
139
-
140
137
  // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
141
138
  // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
142
139
  const Container = PopupContainer;
@@ -158,8 +155,14 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
158
155
  ref(node);
159
156
  }
160
157
  },
161
- 'aria-controls': effectiveIsOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
162
- 'aria-expanded': effectiveIsOpen,
158
+ 'aria-controls': isOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
159
+ // Use ariaAttributes['aria-expanded'] (which comes from `@atlaskit/top-layer`), rather than
160
+ // `isOpen` directly. `@atlaskit/top-layer` tracks the full animation lifecycle via
161
+ // TPopupState and only goes false after the exit animation completes, so:
162
+ // - Screen readers don't announce the popup as closed while it is still visible.
163
+ // - Consumers relying on aria-expanded to show/hide trigger UI don't lose the
164
+ // trigger (popup anchor) before the animation finishes.
165
+ 'aria-expanded': ariaAttributes['aria-expanded'],
163
166
  // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
164
167
  // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
165
168
  // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
@@ -175,7 +178,7 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
175
178
  };
176
179
  return trigger(triggerProps);
177
180
  }), /*#__PURE__*/React.createElement(Popup.Content, _extends({}, roleProps, {
178
- isOpen: effectiveIsOpen,
181
+ isOpen: isOpen,
179
182
  animate: animation,
180
183
  testId: testId && `${testId}--content`,
181
184
  widthFromAnchor: shouldFitContainer ? 'match-anchor' : 'none'
@@ -65,23 +65,45 @@ export const useCloseManager = ({
65
65
  if (!doesDomNodeExist) {
66
66
  return;
67
67
  }
68
- if (isLayerDisabled()) {
68
+ if (fg('jsmgrowth_popup_fix')) {
69
69
  if (target instanceof HTMLElement) {
70
70
  var _target$closest;
71
71
  const layeredElement = (_target$closest = target.closest) === null || _target$closest === void 0 ? void 0 : _target$closest.call(target, `[data-ds--level]`);
72
72
  if (layeredElement) {
73
- const closeType = layeredElement.getAttribute('[data-ds--close--type]');
74
- if (closeType === 'single') {
73
+ const closeType = layeredElement.getAttribute('data-ds--close--type');
74
+ if (closeType === 'single' && isLayerDisabled()) {
75
75
  // if the close type is single, we won't close other disabled layers when clicking outside
76
76
  return;
77
77
  }
78
78
  const levelOfClickedLayer = layeredElement.getAttribute('data-ds--level');
79
79
  if (levelOfClickedLayer && Number(levelOfClickedLayer) > currentLevel) {
80
- // won't trigger onClick event when we click in a higher layer.
80
+ // Don't close this popup when the click landed inside a higher-level layer
81
+ // (e.g. a Drawer or Modal that portals to <body>). Such nodes are not DOM
82
+ // descendants of popupRef, so the contains() checks below would wrongly
83
+ // classify them as "outside" clicks and close the popup prematurely.
81
84
  return;
82
85
  }
83
86
  }
84
87
  }
88
+ } else {
89
+ if (isLayerDisabled()) {
90
+ if (target instanceof HTMLElement) {
91
+ var _target$closest2;
92
+ const layeredElement = (_target$closest2 = target.closest) === null || _target$closest2 === void 0 ? void 0 : _target$closest2.call(target, `[data-ds--level]`);
93
+ if (layeredElement) {
94
+ const closeType = layeredElement.getAttribute('[data-ds--close--type]');
95
+ if (closeType === 'single') {
96
+ // if the close type is single, we won't close other disabled layers when clicking outside
97
+ return;
98
+ }
99
+ const levelOfClickedLayer = layeredElement.getAttribute('data-ds--level');
100
+ if (levelOfClickedLayer && Number(levelOfClickedLayer) > currentLevel) {
101
+ // won't trigger onClick event when we click in a higher layer.
102
+ return;
103
+ }
104
+ }
105
+ }
106
+ }
85
107
  }
86
108
  const isClickOnPopup = popupRef && popupRef.contains(target);
87
109
  const isClickOnTrigger = triggerRef && triggerRef.contains(target);
@@ -118,9 +118,6 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
118
118
  titleId: titleId
119
119
  });
120
120
 
121
- // Sync controlled isOpen with internal state
122
- var effectiveIsOpen = isOpen;
123
-
124
121
  // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
125
122
  // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
126
123
  var Container = PopupContainer;
@@ -141,8 +138,14 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
141
138
  _ref4(node);
142
139
  }
143
140
  },
144
- 'aria-controls': effectiveIsOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
145
- 'aria-expanded': effectiveIsOpen,
141
+ 'aria-controls': isOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
142
+ // Use ariaAttributes['aria-expanded'] (which comes from `@atlaskit/top-layer`), rather than
143
+ // `isOpen` directly. `@atlaskit/top-layer` tracks the full animation lifecycle via
144
+ // TPopupState and only goes false after the exit animation completes, so:
145
+ // - Screen readers don't announce the popup as closed while it is still visible.
146
+ // - Consumers relying on aria-expanded to show/hide trigger UI don't lose the
147
+ // trigger (popup anchor) before the animation finishes.
148
+ 'aria-expanded': ariaAttributes['aria-expanded'],
146
149
  // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
147
150
  // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
148
151
  // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
@@ -158,7 +161,7 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
158
161
  };
159
162
  return trigger(triggerProps);
160
163
  }), /*#__PURE__*/React.createElement(Popup.Content, _extends({}, roleProps, {
161
- isOpen: effectiveIsOpen,
164
+ isOpen: isOpen,
162
165
  animate: animation,
163
166
  testId: testId && "".concat(testId, "--content"),
164
167
  widthFromAnchor: shouldFitContainer ? 'match-anchor' : 'none'
@@ -60,23 +60,45 @@ export var useCloseManager = function useCloseManager(_ref) {
60
60
  if (!doesDomNodeExist) {
61
61
  return;
62
62
  }
63
- if (isLayerDisabled()) {
63
+ if (fg('jsmgrowth_popup_fix')) {
64
64
  if (target instanceof HTMLElement) {
65
65
  var _target$closest;
66
66
  var layeredElement = (_target$closest = target.closest) === null || _target$closest === void 0 ? void 0 : _target$closest.call(target, "[data-ds--level]");
67
67
  if (layeredElement) {
68
- var closeType = layeredElement.getAttribute('[data-ds--close--type]');
69
- if (closeType === 'single') {
68
+ var closeType = layeredElement.getAttribute('data-ds--close--type');
69
+ if (closeType === 'single' && isLayerDisabled()) {
70
70
  // if the close type is single, we won't close other disabled layers when clicking outside
71
71
  return;
72
72
  }
73
73
  var levelOfClickedLayer = layeredElement.getAttribute('data-ds--level');
74
74
  if (levelOfClickedLayer && Number(levelOfClickedLayer) > currentLevel) {
75
- // won't trigger onClick event when we click in a higher layer.
75
+ // Don't close this popup when the click landed inside a higher-level layer
76
+ // (e.g. a Drawer or Modal that portals to <body>). Such nodes are not DOM
77
+ // descendants of popupRef, so the contains() checks below would wrongly
78
+ // classify them as "outside" clicks and close the popup prematurely.
76
79
  return;
77
80
  }
78
81
  }
79
82
  }
83
+ } else {
84
+ if (isLayerDisabled()) {
85
+ if (target instanceof HTMLElement) {
86
+ var _target$closest2;
87
+ var _layeredElement = (_target$closest2 = target.closest) === null || _target$closest2 === void 0 ? void 0 : _target$closest2.call(target, "[data-ds--level]");
88
+ if (_layeredElement) {
89
+ var _closeType = _layeredElement.getAttribute('[data-ds--close--type]');
90
+ if (_closeType === 'single') {
91
+ // if the close type is single, we won't close other disabled layers when clicking outside
92
+ return;
93
+ }
94
+ var _levelOfClickedLayer = _layeredElement.getAttribute('data-ds--level');
95
+ if (_levelOfClickedLayer && Number(_levelOfClickedLayer) > currentLevel) {
96
+ // won't trigger onClick event when we click in a higher layer.
97
+ return;
98
+ }
99
+ }
100
+ }
101
+ }
80
102
  }
81
103
  var isClickOnPopup = popupRef && popupRef.contains(target);
82
104
  var isClickOnTrigger = triggerRef && triggerRef.contains(target);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/popup",
3
- "version": "4.21.2",
3
+ "version": "4.22.1",
4
4
  "description": "A popup displays brief content in an overlay.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -48,7 +48,7 @@
48
48
  "@atlaskit/portal": "^5.5.0",
49
49
  "@atlaskit/primitives": "^19.0.0",
50
50
  "@atlaskit/tokens": "^13.0.0",
51
- "@atlaskit/top-layer": "^0.11.0",
51
+ "@atlaskit/top-layer": "^0.12.0",
52
52
  "@babel/runtime": "^7.0.0",
53
53
  "@compiled/react": "^0.20.0",
54
54
  "bind-event-listener": "^3.0.0",
@@ -69,12 +69,12 @@
69
69
  "@atlaskit/docs": "^11.8.0",
70
70
  "@atlaskit/form": "^15.5.0",
71
71
  "@atlaskit/heading": "^5.4.0",
72
- "@atlaskit/icon": "^35.1.0",
72
+ "@atlaskit/icon": "^35.3.0",
73
73
  "@atlaskit/link": "^3.4.0",
74
74
  "@atlaskit/modal-dialog": "^15.2.0",
75
75
  "@atlaskit/section-message": "^8.13.0",
76
76
  "@atlaskit/textfield": "^8.3.0",
77
- "@atlaskit/toggle": "^15.6.0",
77
+ "@atlaskit/toggle": "^16.0.0",
78
78
  "@atlassian/feature-flags-test-utils": "^1.1.0",
79
79
  "@atlassian/react-compiler-gating": "workspace:^",
80
80
  "@atlassian/ssr-tests": "workspace:^",
@@ -114,6 +114,9 @@
114
114
  }
115
115
  },
116
116
  "platform-feature-flags": {
117
+ "jsmgrowth_popup_fix": {
118
+ "type": "boolean"
119
+ },
117
120
  "platform-dst-top-layer": {
118
121
  "type": "boolean"
119
122
  },