@atlaskit/popup 4.23.1 → 4.23.3

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,22 @@
1
1
  # @atlaskit/popup
2
2
 
3
+ ## 4.23.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e5b4070f51a93`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e5b4070f51a93) -
8
+ Remove Top Layer compatibility aliases and expose close-event helpers from the `dialog` and
9
+ `popover` entry points.
10
+ - Updated dependencies
11
+
12
+ ## 4.23.2
13
+
14
+ ### Patch Changes
15
+
16
+ - [`021a645c6f39a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/021a645c6f39a) -
17
+ Update internal top-layer adopter code paths behind `platform-dst-top-layer`.
18
+ - Updated dependencies
19
+
3
20
  ## 4.23.1
4
21
 
5
22
  ### Patch Changes
@@ -15,10 +15,9 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
15
15
  var _runtime = require("@compiled/react/runtime");
16
16
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
17
17
  var _animations = require("@atlaskit/top-layer/animations");
18
- var _createCloseEvent = require("@atlaskit/top-layer/create-close-event");
19
18
  var _placementMap = require("@atlaskit/top-layer/placement-map");
20
19
  var _popover = require("@atlaskit/top-layer/popover");
21
- var _popupSurface = require("@atlaskit/top-layer/popup-surface");
20
+ var _popoverSurface = require("@atlaskit/top-layer/popover-surface");
22
21
  var _useAnchorPosition = require("@atlaskit/top-layer/use-anchor-position");
23
22
  var _useWidthFromAnchor = require("@atlaskit/top-layer/use-width-from-anchor");
24
23
  var _topLayerBridge = require("../internal/top-layer-bridge");
@@ -87,7 +86,7 @@ function PopupContentTopLayer(_ref) {
87
86
  if (!onClose) {
88
87
  return;
89
88
  }
90
- onClose((0, _createCloseEvent.createPopoverCloseEvent)({
89
+ onClose((0, _popover.createPopoverCloseEvent)({
91
90
  reason: reason
92
91
  }));
93
92
  }, [onClose]);
@@ -139,7 +138,7 @@ function PopupContentTopLayer(_ref) {
139
138
  "data-testid": testId,
140
139
  tabIndex: autoFocus ? -1 : undefined,
141
140
  xcss: xcss
142
- }, children(contentProps)) : /*#__PURE__*/React.createElement(_popupSurface.PopupSurface, null, /*#__PURE__*/React.createElement("div", {
141
+ }, children(contentProps)) : /*#__PURE__*/React.createElement(_popoverSurface.PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
143
142
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
144
143
  className: (0, _runtime.ax)([shouldFitViewport && "_1reo1wug _18m91wug", (0, _runtime.ax)([xcss])])
145
144
  }, children(contentProps))));
@@ -15,9 +15,13 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
15
15
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
16
16
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
17
17
  var _animations = require("@atlaskit/top-layer/animations");
18
- var _createCloseEvent = require("@atlaskit/top-layer/create-close-event");
18
+ var _getAriaForTrigger = require("@atlaskit/top-layer/get-aria-for-trigger");
19
19
  var _placementMap = require("@atlaskit/top-layer/placement-map");
20
- var _popup = require("@atlaskit/top-layer/popup");
20
+ var _popover = require("@atlaskit/top-layer/popover");
21
+ var _popoverSurface = require("@atlaskit/top-layer/popover-surface");
22
+ var _useAnchorPosition = require("@atlaskit/top-layer/use-anchor-position");
23
+ var _usePopoverId = require("@atlaskit/top-layer/use-popover-id");
24
+ var _useWidthFromAnchor = require("@atlaskit/top-layer/use-width-from-anchor");
21
25
  var _topLayerBridge = require("./internal/top-layer-bridge");
22
26
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
23
27
  var contentOverflowStyles = {
@@ -39,6 +43,7 @@ var EMPTY_STYLE = {};
39
43
  * Gated behind the `platform-dst-top-layer` feature flag.
40
44
  */
41
45
  var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(function PopupTopLayer(_ref) {
46
+ var _roleProps$role;
42
47
  var xcss = _ref.xcss,
43
48
  isOpen = _ref.isOpen,
44
49
  providedId = _ref.id,
@@ -73,12 +78,14 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
73
78
  shouldFitViewport = _ref.shouldFitViewport,
74
79
  _appearance = _ref.appearance;
75
80
  var triggerRef = (0, _react.useRef)(null);
81
+ var popoverRef = (0, _react.useRef)(null);
76
82
  var popupContainerRef = (0, _react.useRef)(null);
77
83
  var _useState = (0, _react.useState)(null),
78
84
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
79
85
  setInitialFocusRef = _useState2[1];
86
+ var popoverId = (0, _usePopoverId.usePopoverId)();
80
87
 
81
- // ── Placement conversion ──
88
+ // Placement conversion.
82
89
  // Legacy `offset` is the popper `[along, away]` tuple; `fromLegacyPlacement`
83
90
  // folds it into the new `placement.offset` shape so it travels with placement.
84
91
  var topLayerPlacement = (0, _react.useMemo)(function () {
@@ -87,28 +94,38 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
87
94
  offset: offsetProp
88
95
  });
89
96
  }, [placement, offsetProp]);
97
+ (0, _useAnchorPosition.useAnchorPosition)({
98
+ anchorRef: triggerRef,
99
+ popoverRef: popoverRef,
100
+ placement: topLayerPlacement
101
+ });
102
+ (0, _useWidthFromAnchor.useWidthFromAnchor)({
103
+ mode: shouldFitContainer ? 'match-anchor' : 'none',
104
+ popoverRef: popoverRef,
105
+ anchorRef: triggerRef
106
+ });
90
107
 
91
- // ── onClose bridge ──
108
+ // onClose bridge.
92
109
  // Translates top-layer's { reason: TPopoverCloseReason } into legacy
93
110
  // onClose(event, currentLevel?) by synthesizing DOM events.
94
111
  //
95
112
  // Focus restoration is handled natively by the Popover API:
96
- // - Escape → browser restores focus to the trigger automatically
97
- // - Click-outside → browser does NOT restore (correct behavior)
113
+ // - Escape: browser restores focus to the trigger automatically
114
+ // - Click-outside: browser does NOT restore (correct behavior)
98
115
  // No manual triggerRef.current?.focus() is needed.
99
116
  var handleOnClose = (0, _react.useCallback)(function (_ref2) {
100
117
  var reason = _ref2.reason;
101
118
  if (!onClose) {
102
119
  return;
103
120
  }
104
- onClose((0, _createCloseEvent.createPopoverCloseEvent)({
121
+ onClose((0, _popover.createPopoverCloseEvent)({
105
122
  reason: reason
106
123
  }));
107
124
  }, [onClose]);
108
125
 
109
- // ── Content render-prop bridge ──
126
+ // Content render-prop bridge.
110
127
  // Legacy content() receives { isOpen, update, setInitialFocusRef, onClose }.
111
- // update is a no-op — CSS Anchor Positioning self-updates.
128
+ // update is a no-op because CSS Anchor Positioning self-updates.
112
129
  var contentProps = (0, _react.useMemo)(function () {
113
130
  return {
114
131
  isOpen: isOpen,
@@ -118,62 +135,50 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
118
135
  };
119
136
  }, [isOpen, onClose]);
120
137
 
121
- // ── Role mapping ──
122
- // Build the role/label props for PopupContent.
138
+ // Role mapping.
139
+ // Build the role/label props for Popover.
123
140
  // Roles requiring accessible names must have label or labelledBy.
124
141
  var roleProps = (0, _topLayerBridge.useRoleProps)({
125
142
  role: role,
126
143
  label: label,
127
144
  titleId: titleId
128
145
  });
146
+ var ariaAttributes = (0, _getAriaForTrigger.getAriaForTrigger)({
147
+ role: (_roleProps$role = roleProps.role) !== null && _roleProps$role !== void 0 ? _roleProps$role : 'dialog',
148
+ isOpen: isOpen,
149
+ popoverId: popoverId
150
+ });
129
151
 
130
152
  // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
131
153
  // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
132
154
  var Container = PopupContainer;
133
- return /*#__PURE__*/React.createElement(_popup.Popup, {
134
- placement: topLayerPlacement,
135
- onClose: handleOnClose,
136
- testId: testId
137
- }, /*#__PURE__*/React.createElement(_popup.Popup.TriggerFunction, null, function (_ref3) {
138
- var _ref4 = _ref3.ref,
139
- _toggle = _ref3.toggle,
140
- ariaAttributes = _ref3.ariaAttributes,
141
- popoverId = _ref3.popoverId;
142
- // Map to legacy TriggerProps
143
- var triggerProps = {
144
- ref: function ref(node) {
145
- triggerRef.current = node;
146
- if (typeof _ref4 === 'function') {
147
- _ref4(node);
148
- }
149
- },
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'],
158
- // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
159
- // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
160
- // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
161
- // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
162
- // top-layer API surface is not yet settled. Widening adopter types now would commit
163
- // us to a public surface we may revisit. The runtime value is unchanged; only the
164
- // TypeScript-visible type is narrowed at this boundary.
165
- // REMOVE WHEN: the top-layer public API is committed (see
166
- // packages/design-system/top-layer/notes/decisions/migration-roadmap.md "Open API
167
- // decisions deferred to a follow-up PR") and a follow-up `minor` PR widens
168
- // `TriggerProps['aria-haspopup']` to match.
169
- 'aria-haspopup': ariaAttributes['aria-haspopup']
170
- };
171
- return trigger(triggerProps);
172
- }), /*#__PURE__*/React.createElement(_popup.Popup.Content, (0, _extends2.default)({}, roleProps, {
155
+
156
+ // Map to legacy TriggerProps.
157
+ var triggerProps = {
158
+ ref: function ref(node) {
159
+ triggerRef.current = node;
160
+ },
161
+ 'aria-controls': providedId !== null && providedId !== void 0 ? providedId : popoverId,
162
+ // `aria-expanded` reflects current open state.
163
+ 'aria-expanded': ariaAttributes['aria-expanded'],
164
+ // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
165
+ // `getAriaForTrigger` derives `aria-haspopup` from the content's role and types it
166
+ // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
167
+ // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
168
+ // top-layer API surface is not yet settled. Widening adopter types now would commit
169
+ // us to a public surface we may revisit. The runtime value is unchanged; only the
170
+ // TypeScript-visible type is narrowed at this boundary.
171
+ 'aria-haspopup': ariaAttributes['aria-haspopup']
172
+ };
173
+ return /*#__PURE__*/React.createElement(_react.Fragment, null, trigger(triggerProps), /*#__PURE__*/React.createElement(_popover.Popover, (0, _extends2.default)({
174
+ ref: popoverRef,
175
+ id: providedId !== null && providedId !== void 0 ? providedId : popoverId
176
+ }, roleProps, {
173
177
  isOpen: isOpen,
178
+ onClose: handleOnClose,
174
179
  animate: animation,
175
- testId: testId && "".concat(testId, "--content"),
176
- widthFromAnchor: shouldFitContainer ? 'match-anchor' : 'none'
180
+ placement: topLayerPlacement,
181
+ testId: testId && "".concat(testId, "--content")
177
182
  }), Container ? /*#__PURE__*/React.createElement(Container, {
178
183
  ref: popupContainerRef,
179
184
  style: EMPTY_STYLE,
@@ -182,7 +187,7 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
182
187
  "data-testid": testId,
183
188
  tabIndex: autoFocus ? -1 : undefined,
184
189
  xcss: xcss
185
- }, content(contentProps)) : /*#__PURE__*/React.createElement(_popup.Popup.Surface, null, /*#__PURE__*/React.createElement("div", {
190
+ }, content(contentProps)) : /*#__PURE__*/React.createElement(_popoverSurface.PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
186
191
  className: (0, _runtime.ax)([contentOverflowStyles[shouldFitViewport ? 'fitViewport' : 'default']])
187
192
  }, content(contentProps)))));
188
193
  });
@@ -6,10 +6,9 @@ import { useCallback, useContext, useMemo, useRef, useState } from 'react';
6
6
  import { ax, ix } from '@compiled/react/runtime';
7
7
  import noop from '@atlaskit/ds-lib/noop';
8
8
  import { slideAndFade } from '@atlaskit/top-layer/animations';
9
- import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
10
9
  import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
11
- import { Popover } from '@atlaskit/top-layer/popover';
12
- import { PopupSurface } from '@atlaskit/top-layer/popup-surface';
10
+ import { createPopoverCloseEvent, Popover } from '@atlaskit/top-layer/popover';
11
+ import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
13
12
  import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
14
13
  import { useWidthFromAnchor } from '@atlaskit/top-layer/use-width-from-anchor';
15
14
  import { useRoleProps } from '../internal/top-layer-bridge';
@@ -144,7 +143,7 @@ export function PopupContentTopLayer({
144
143
  "data-testid": testId,
145
144
  tabIndex: autoFocus ? -1 : undefined,
146
145
  xcss: xcss
147
- }, children(contentProps)) : /*#__PURE__*/React.createElement(PopupSurface, null, /*#__PURE__*/React.createElement("div", {
146
+ }, children(contentProps)) : /*#__PURE__*/React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
148
147
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
149
148
  className: ax([shouldFitViewport && "_1reo1wug _18m91wug", ax([xcss])])
150
149
  }, children(contentProps))));
@@ -3,12 +3,16 @@ import _extends from "@babel/runtime/helpers/extends";
3
3
  import "./popup-top-layer.compiled.css";
4
4
  import * as React from 'react';
5
5
  import { ax, ix } from "@compiled/react/runtime";
6
- import { memo, useCallback, useMemo, useRef, useState } from 'react';
6
+ import { Fragment, memo, useCallback, useMemo, useRef, useState } from 'react';
7
7
  import noop from '@atlaskit/ds-lib/noop';
8
8
  import { slideAndFade } from '@atlaskit/top-layer/animations';
9
- import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
9
+ import { getAriaForTrigger } from '@atlaskit/top-layer/get-aria-for-trigger';
10
10
  import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
11
- import { Popup } from '@atlaskit/top-layer/popup';
11
+ import { createPopoverCloseEvent, Popover } from '@atlaskit/top-layer/popover';
12
+ import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
13
+ import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
14
+ import { usePopoverId } from '@atlaskit/top-layer/use-popover-id';
15
+ import { useWidthFromAnchor } from '@atlaskit/top-layer/use-width-from-anchor';
12
16
  import { useRoleProps } from './internal/top-layer-bridge';
13
17
  const contentOverflowStyles = {
14
18
  fitViewport: "_1reo1wug _18m91wug",
@@ -49,9 +53,9 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
49
53
  role,
50
54
  label,
51
55
  titleId,
52
- // ── No-op props ──
56
+ // No-op props.
53
57
  // These props are accepted for API compatibility but have no effect
54
- // in the top-layer path. Each is documented with why it's unnecessary.
58
+ // in the top-layer path. Each is documented with why it is unnecessary.
55
59
 
56
60
  // top-layer: stacking managed by browser top layer. zIndex is a no-op.
57
61
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -84,25 +88,38 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
84
88
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
89
  appearance: _appearance
86
90
  }) {
91
+ var _roleProps$role;
87
92
  const triggerRef = useRef(null);
93
+ const popoverRef = useRef(null);
88
94
  const popupContainerRef = useRef(null);
89
95
  const [, setInitialFocusRef] = useState(null);
96
+ const popoverId = usePopoverId();
90
97
 
91
- // ── Placement conversion ──
98
+ // Placement conversion.
92
99
  // Legacy `offset` is the popper `[along, away]` tuple; `fromLegacyPlacement`
93
100
  // folds it into the new `placement.offset` shape so it travels with placement.
94
101
  const topLayerPlacement = useMemo(() => fromLegacyPlacement({
95
102
  legacy: placement,
96
103
  offset: offsetProp
97
104
  }), [placement, offsetProp]);
105
+ useAnchorPosition({
106
+ anchorRef: triggerRef,
107
+ popoverRef,
108
+ placement: topLayerPlacement
109
+ });
110
+ useWidthFromAnchor({
111
+ mode: shouldFitContainer ? 'match-anchor' : 'none',
112
+ popoverRef,
113
+ anchorRef: triggerRef
114
+ });
98
115
 
99
- // ── onClose bridge ──
116
+ // onClose bridge.
100
117
  // Translates top-layer's { reason: TPopoverCloseReason } into legacy
101
118
  // onClose(event, currentLevel?) by synthesizing DOM events.
102
119
  //
103
120
  // Focus restoration is handled natively by the Popover API:
104
- // - Escape → browser restores focus to the trigger automatically
105
- // - Click-outside → browser does NOT restore (correct behavior)
121
+ // - Escape: browser restores focus to the trigger automatically
122
+ // - Click-outside: browser does NOT restore (correct behavior)
106
123
  // No manual triggerRef.current?.focus() is needed.
107
124
  const handleOnClose = useCallback(({
108
125
  reason
@@ -115,9 +132,9 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
115
132
  }));
116
133
  }, [onClose]);
117
134
 
118
- // ── Content render-prop bridge ──
135
+ // Content render-prop bridge.
119
136
  // Legacy content() receives { isOpen, update, setInitialFocusRef, onClose }.
120
- // update is a no-op — CSS Anchor Positioning self-updates.
137
+ // update is a no-op because CSS Anchor Positioning self-updates.
121
138
  const contentProps = useMemo(() => ({
122
139
  isOpen: isOpen,
123
140
  update: noop,
@@ -125,63 +142,50 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
125
142
  setInitialFocusRef
126
143
  }), [isOpen, onClose]);
127
144
 
128
- // ── Role mapping ──
129
- // Build the role/label props for PopupContent.
145
+ // Role mapping.
146
+ // Build the role/label props for Popover.
130
147
  // Roles requiring accessible names must have label or labelledBy.
131
148
  const roleProps = useRoleProps({
132
149
  role,
133
150
  label,
134
151
  titleId
135
152
  });
153
+ const ariaAttributes = getAriaForTrigger({
154
+ role: (_roleProps$role = roleProps.role) !== null && _roleProps$role !== void 0 ? _roleProps$role : 'dialog',
155
+ isOpen,
156
+ popoverId
157
+ });
136
158
 
137
159
  // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
138
160
  // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
139
161
  const Container = PopupContainer;
140
- return /*#__PURE__*/React.createElement(Popup, {
141
- placement: topLayerPlacement,
142
- onClose: handleOnClose,
143
- testId: testId
144
- }, /*#__PURE__*/React.createElement(Popup.TriggerFunction, null, ({
145
- ref,
146
- toggle: _toggle,
147
- ariaAttributes,
148
- popoverId
149
- }) => {
150
- // Map to legacy TriggerProps
151
- const triggerProps = {
152
- ref: node => {
153
- triggerRef.current = node;
154
- if (typeof ref === 'function') {
155
- ref(node);
156
- }
157
- },
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'],
166
- // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
167
- // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
168
- // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
169
- // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
170
- // top-layer API surface is not yet settled. Widening adopter types now would commit
171
- // us to a public surface we may revisit. The runtime value is unchanged; only the
172
- // TypeScript-visible type is narrowed at this boundary.
173
- // REMOVE WHEN: the top-layer public API is committed (see
174
- // packages/design-system/top-layer/notes/decisions/migration-roadmap.md "Open API
175
- // decisions deferred to a follow-up PR") and a follow-up `minor` PR widens
176
- // `TriggerProps['aria-haspopup']` to match.
177
- 'aria-haspopup': ariaAttributes['aria-haspopup']
178
- };
179
- return trigger(triggerProps);
180
- }), /*#__PURE__*/React.createElement(Popup.Content, _extends({}, roleProps, {
162
+
163
+ // Map to legacy TriggerProps.
164
+ const triggerProps = {
165
+ ref: node => {
166
+ triggerRef.current = node;
167
+ },
168
+ 'aria-controls': providedId !== null && providedId !== void 0 ? providedId : popoverId,
169
+ // `aria-expanded` reflects current open state.
170
+ 'aria-expanded': ariaAttributes['aria-expanded'],
171
+ // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
172
+ // `getAriaForTrigger` derives `aria-haspopup` from the content's role and types it
173
+ // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
174
+ // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
175
+ // top-layer API surface is not yet settled. Widening adopter types now would commit
176
+ // us to a public surface we may revisit. The runtime value is unchanged; only the
177
+ // TypeScript-visible type is narrowed at this boundary.
178
+ 'aria-haspopup': ariaAttributes['aria-haspopup']
179
+ };
180
+ return /*#__PURE__*/React.createElement(Fragment, null, trigger(triggerProps), /*#__PURE__*/React.createElement(Popover, _extends({
181
+ ref: popoverRef,
182
+ id: providedId !== null && providedId !== void 0 ? providedId : popoverId
183
+ }, roleProps, {
181
184
  isOpen: isOpen,
185
+ onClose: handleOnClose,
182
186
  animate: animation,
183
- testId: testId && `${testId}--content`,
184
- widthFromAnchor: shouldFitContainer ? 'match-anchor' : 'none'
187
+ placement: topLayerPlacement,
188
+ testId: testId && `${testId}--content`
185
189
  }), Container ? /*#__PURE__*/React.createElement(Container, {
186
190
  ref: popupContainerRef,
187
191
  style: EMPTY_STYLE,
@@ -190,7 +194,7 @@ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
190
194
  "data-testid": testId,
191
195
  tabIndex: autoFocus ? -1 : undefined,
192
196
  xcss: xcss
193
- }, content(contentProps)) : /*#__PURE__*/React.createElement(Popup.Surface, null, /*#__PURE__*/React.createElement("div", {
197
+ }, content(contentProps)) : /*#__PURE__*/React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
194
198
  className: ax([contentOverflowStyles[shouldFitViewport ? 'fitViewport' : 'default']])
195
199
  }, content(contentProps)))));
196
200
  });
@@ -7,10 +7,9 @@ import { useCallback, useContext, useMemo, useRef, useState } from 'react';
7
7
  import { ax, ix } from '@compiled/react/runtime';
8
8
  import noop from '@atlaskit/ds-lib/noop';
9
9
  import { slideAndFade } from '@atlaskit/top-layer/animations';
10
- import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
11
10
  import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
12
- import { Popover } from '@atlaskit/top-layer/popover';
13
- import { PopupSurface } from '@atlaskit/top-layer/popup-surface';
11
+ import { createPopoverCloseEvent, Popover } from '@atlaskit/top-layer/popover';
12
+ import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
14
13
  import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
15
14
  import { useWidthFromAnchor } from '@atlaskit/top-layer/use-width-from-anchor';
16
15
  import { useRoleProps } from '../internal/top-layer-bridge';
@@ -130,7 +129,7 @@ export function PopupContentTopLayer(_ref) {
130
129
  "data-testid": testId,
131
130
  tabIndex: autoFocus ? -1 : undefined,
132
131
  xcss: xcss
133
- }, children(contentProps)) : /*#__PURE__*/React.createElement(PopupSurface, null, /*#__PURE__*/React.createElement("div", {
132
+ }, children(contentProps)) : /*#__PURE__*/React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
134
133
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
135
134
  className: ax([shouldFitViewport && "_1reo1wug _18m91wug", ax([xcss])])
136
135
  }, children(contentProps))));
@@ -4,12 +4,16 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
4
  import "./popup-top-layer.compiled.css";
5
5
  import * as React from 'react';
6
6
  import { ax, ix } from "@compiled/react/runtime";
7
- import { memo, useCallback, useMemo, useRef, useState } from 'react';
7
+ import { Fragment, memo, useCallback, useMemo, useRef, useState } from 'react';
8
8
  import noop from '@atlaskit/ds-lib/noop';
9
9
  import { slideAndFade } from '@atlaskit/top-layer/animations';
10
- import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
10
+ import { getAriaForTrigger } from '@atlaskit/top-layer/get-aria-for-trigger';
11
11
  import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
12
- import { Popup } from '@atlaskit/top-layer/popup';
12
+ import { createPopoverCloseEvent, Popover } from '@atlaskit/top-layer/popover';
13
+ import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
14
+ import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
15
+ import { usePopoverId } from '@atlaskit/top-layer/use-popover-id';
16
+ import { useWidthFromAnchor } from '@atlaskit/top-layer/use-width-from-anchor';
13
17
  import { useRoleProps } from './internal/top-layer-bridge';
14
18
  var contentOverflowStyles = {
15
19
  fitViewport: "_1reo1wug _18m91wug",
@@ -30,6 +34,7 @@ var EMPTY_STYLE = {};
30
34
  * Gated behind the `platform-dst-top-layer` feature flag.
31
35
  */
32
36
  export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
37
+ var _roleProps$role;
33
38
  var xcss = _ref.xcss,
34
39
  isOpen = _ref.isOpen,
35
40
  providedId = _ref.id,
@@ -64,12 +69,14 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
64
69
  shouldFitViewport = _ref.shouldFitViewport,
65
70
  _appearance = _ref.appearance;
66
71
  var triggerRef = useRef(null);
72
+ var popoverRef = useRef(null);
67
73
  var popupContainerRef = useRef(null);
68
74
  var _useState = useState(null),
69
75
  _useState2 = _slicedToArray(_useState, 2),
70
76
  setInitialFocusRef = _useState2[1];
77
+ var popoverId = usePopoverId();
71
78
 
72
- // ── Placement conversion ──
79
+ // Placement conversion.
73
80
  // Legacy `offset` is the popper `[along, away]` tuple; `fromLegacyPlacement`
74
81
  // folds it into the new `placement.offset` shape so it travels with placement.
75
82
  var topLayerPlacement = useMemo(function () {
@@ -78,14 +85,24 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
78
85
  offset: offsetProp
79
86
  });
80
87
  }, [placement, offsetProp]);
88
+ useAnchorPosition({
89
+ anchorRef: triggerRef,
90
+ popoverRef: popoverRef,
91
+ placement: topLayerPlacement
92
+ });
93
+ useWidthFromAnchor({
94
+ mode: shouldFitContainer ? 'match-anchor' : 'none',
95
+ popoverRef: popoverRef,
96
+ anchorRef: triggerRef
97
+ });
81
98
 
82
- // ── onClose bridge ──
99
+ // onClose bridge.
83
100
  // Translates top-layer's { reason: TPopoverCloseReason } into legacy
84
101
  // onClose(event, currentLevel?) by synthesizing DOM events.
85
102
  //
86
103
  // Focus restoration is handled natively by the Popover API:
87
- // - Escape → browser restores focus to the trigger automatically
88
- // - Click-outside → browser does NOT restore (correct behavior)
104
+ // - Escape: browser restores focus to the trigger automatically
105
+ // - Click-outside: browser does NOT restore (correct behavior)
89
106
  // No manual triggerRef.current?.focus() is needed.
90
107
  var handleOnClose = useCallback(function (_ref2) {
91
108
  var reason = _ref2.reason;
@@ -97,9 +114,9 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
97
114
  }));
98
115
  }, [onClose]);
99
116
 
100
- // ── Content render-prop bridge ──
117
+ // Content render-prop bridge.
101
118
  // Legacy content() receives { isOpen, update, setInitialFocusRef, onClose }.
102
- // update is a no-op — CSS Anchor Positioning self-updates.
119
+ // update is a no-op because CSS Anchor Positioning self-updates.
103
120
  var contentProps = useMemo(function () {
104
121
  return {
105
122
  isOpen: isOpen,
@@ -109,62 +126,50 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
109
126
  };
110
127
  }, [isOpen, onClose]);
111
128
 
112
- // ── Role mapping ──
113
- // Build the role/label props for PopupContent.
129
+ // Role mapping.
130
+ // Build the role/label props for Popover.
114
131
  // Roles requiring accessible names must have label or labelledBy.
115
132
  var roleProps = useRoleProps({
116
133
  role: role,
117
134
  label: label,
118
135
  titleId: titleId
119
136
  });
137
+ var ariaAttributes = getAriaForTrigger({
138
+ role: (_roleProps$role = roleProps.role) !== null && _roleProps$role !== void 0 ? _roleProps$role : 'dialog',
139
+ isOpen: isOpen,
140
+ popoverId: popoverId
141
+ });
120
142
 
121
143
  // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
122
144
  // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
123
145
  var Container = PopupContainer;
124
- return /*#__PURE__*/React.createElement(Popup, {
125
- placement: topLayerPlacement,
126
- onClose: handleOnClose,
127
- testId: testId
128
- }, /*#__PURE__*/React.createElement(Popup.TriggerFunction, null, function (_ref3) {
129
- var _ref4 = _ref3.ref,
130
- _toggle = _ref3.toggle,
131
- ariaAttributes = _ref3.ariaAttributes,
132
- popoverId = _ref3.popoverId;
133
- // Map to legacy TriggerProps
134
- var triggerProps = {
135
- ref: function ref(node) {
136
- triggerRef.current = node;
137
- if (typeof _ref4 === 'function') {
138
- _ref4(node);
139
- }
140
- },
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'],
149
- // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
150
- // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
151
- // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
152
- // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
153
- // top-layer API surface is not yet settled. Widening adopter types now would commit
154
- // us to a public surface we may revisit. The runtime value is unchanged; only the
155
- // TypeScript-visible type is narrowed at this boundary.
156
- // REMOVE WHEN: the top-layer public API is committed (see
157
- // packages/design-system/top-layer/notes/decisions/migration-roadmap.md "Open API
158
- // decisions deferred to a follow-up PR") and a follow-up `minor` PR widens
159
- // `TriggerProps['aria-haspopup']` to match.
160
- 'aria-haspopup': ariaAttributes['aria-haspopup']
161
- };
162
- return trigger(triggerProps);
163
- }), /*#__PURE__*/React.createElement(Popup.Content, _extends({}, roleProps, {
146
+
147
+ // Map to legacy TriggerProps.
148
+ var triggerProps = {
149
+ ref: function ref(node) {
150
+ triggerRef.current = node;
151
+ },
152
+ 'aria-controls': providedId !== null && providedId !== void 0 ? providedId : popoverId,
153
+ // `aria-expanded` reflects current open state.
154
+ 'aria-expanded': ariaAttributes['aria-expanded'],
155
+ // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
156
+ // `getAriaForTrigger` derives `aria-haspopup` from the content's role and types it
157
+ // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
158
+ // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
159
+ // top-layer API surface is not yet settled. Widening adopter types now would commit
160
+ // us to a public surface we may revisit. The runtime value is unchanged; only the
161
+ // TypeScript-visible type is narrowed at this boundary.
162
+ 'aria-haspopup': ariaAttributes['aria-haspopup']
163
+ };
164
+ return /*#__PURE__*/React.createElement(Fragment, null, trigger(triggerProps), /*#__PURE__*/React.createElement(Popover, _extends({
165
+ ref: popoverRef,
166
+ id: providedId !== null && providedId !== void 0 ? providedId : popoverId
167
+ }, roleProps, {
164
168
  isOpen: isOpen,
169
+ onClose: handleOnClose,
165
170
  animate: animation,
166
- testId: testId && "".concat(testId, "--content"),
167
- widthFromAnchor: shouldFitContainer ? 'match-anchor' : 'none'
171
+ placement: topLayerPlacement,
172
+ testId: testId && "".concat(testId, "--content")
168
173
  }), Container ? /*#__PURE__*/React.createElement(Container, {
169
174
  ref: popupContainerRef,
170
175
  style: EMPTY_STYLE,
@@ -173,7 +178,7 @@ export var PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer(_ref) {
173
178
  "data-testid": testId,
174
179
  tabIndex: autoFocus ? -1 : undefined,
175
180
  xcss: xcss
176
- }, content(contentProps)) : /*#__PURE__*/React.createElement(Popup.Surface, null, /*#__PURE__*/React.createElement("div", {
181
+ }, content(contentProps)) : /*#__PURE__*/React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
177
182
  className: ax([contentOverflowStyles[shouldFitViewport ? 'fitViewport' : 'default']])
178
183
  }, content(contentProps)))));
179
184
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/popup",
3
- "version": "4.23.1",
3
+ "version": "4.23.3",
4
4
  "description": "A popup displays brief content in an overlay.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -41,14 +41,14 @@
41
41
  "dependencies": {
42
42
  "@atlaskit/css": "^0.19.0",
43
43
  "@atlaskit/ds-lib": "^7.0.0",
44
- "@atlaskit/layering": "^3.7.0",
44
+ "@atlaskit/layering": "^3.8.0",
45
45
  "@atlaskit/motion": "^6.2.0",
46
46
  "@atlaskit/platform-feature-flags": "^1.1.0",
47
47
  "@atlaskit/popper": "^7.2.0",
48
48
  "@atlaskit/portal": "^5.5.0",
49
49
  "@atlaskit/primitives": "^19.0.0",
50
- "@atlaskit/tokens": "^13.1.0",
51
- "@atlaskit/top-layer": "^0.13.0",
50
+ "@atlaskit/tokens": "^13.3.0",
51
+ "@atlaskit/top-layer": "^0.15.0",
52
52
  "@babel/runtime": "^7.0.0",
53
53
  "@compiled/react": "^0.20.0",
54
54
  "bind-event-listener": "^3.0.0",