@atlaskit/popup 4.23.0 → 4.23.2

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