@atlaskit/popup 4.16.6 → 4.18.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/compositional/popup/package.json +17 -0
  3. package/compositional/popup-content/package.json +17 -0
  4. package/compositional/popup-trigger/package.json +17 -0
  5. package/dist/cjs/compositional/popup-content-top-layer.compiled.css +2 -0
  6. package/dist/cjs/compositional/popup-content-top-layer.js +135 -0
  7. package/dist/cjs/compositional/popup-content.js +36 -0
  8. package/dist/cjs/entry-points/compositional-popup-content.js +12 -0
  9. package/dist/cjs/entry-points/compositional-popup-trigger.js +12 -0
  10. package/dist/cjs/entry-points/compositional-popup.js +12 -0
  11. package/dist/cjs/entry-points/popup.js +12 -0
  12. package/dist/cjs/entry-points/types.js +1 -0
  13. package/dist/cjs/internal/top-layer-bridge.js +55 -0
  14. package/dist/cjs/popper-wrapper.js +13 -8
  15. package/dist/cjs/popup-top-layer.compiled.css +2 -0
  16. package/dist/cjs/popup-top-layer.js +185 -0
  17. package/dist/cjs/popup.js +63 -45
  18. package/dist/es2019/compositional/popup-content-top-layer.compiled.css +2 -0
  19. package/dist/es2019/compositional/popup-content-top-layer.js +140 -0
  20. package/dist/es2019/compositional/popup-content.js +36 -0
  21. package/dist/es2019/entry-points/compositional-popup-content.js +1 -0
  22. package/dist/es2019/entry-points/compositional-popup-trigger.js +1 -0
  23. package/dist/es2019/entry-points/compositional-popup.js +1 -0
  24. package/dist/es2019/entry-points/popup.js +1 -0
  25. package/dist/es2019/entry-points/types.js +0 -0
  26. package/dist/es2019/internal/top-layer-bridge.js +51 -0
  27. package/dist/es2019/popper-wrapper.js +11 -8
  28. package/dist/es2019/popup-top-layer.compiled.css +2 -0
  29. package/dist/es2019/popup-top-layer.js +193 -0
  30. package/dist/es2019/popup.js +52 -33
  31. package/dist/esm/compositional/popup-content-top-layer.compiled.css +2 -0
  32. package/dist/esm/compositional/popup-content-top-layer.js +126 -0
  33. package/dist/esm/compositional/popup-content.js +36 -0
  34. package/dist/esm/entry-points/compositional-popup-content.js +1 -0
  35. package/dist/esm/entry-points/compositional-popup-trigger.js +1 -0
  36. package/dist/esm/entry-points/compositional-popup.js +1 -0
  37. package/dist/esm/entry-points/popup.js +1 -0
  38. package/dist/esm/entry-points/types.js +0 -0
  39. package/dist/esm/internal/top-layer-bridge.js +50 -0
  40. package/dist/esm/popper-wrapper.js +13 -8
  41. package/dist/esm/popup-top-layer.compiled.css +2 -0
  42. package/dist/esm/popup-top-layer.js +176 -0
  43. package/dist/esm/popup.js +63 -45
  44. package/dist/types/compositional/popup-content-top-layer.d.ts +21 -0
  45. package/dist/types/compositional/popup-content.d.ts +2 -2
  46. package/dist/types/entry-points/compositional-popup-content.d.ts +2 -0
  47. package/dist/types/entry-points/compositional-popup-trigger.d.ts +2 -0
  48. package/dist/types/entry-points/compositional-popup.d.ts +2 -0
  49. package/dist/types/entry-points/popup.d.ts +1 -0
  50. package/dist/types/entry-points/types.d.ts +1 -0
  51. package/dist/types/internal/top-layer-bridge.d.ts +29 -0
  52. package/dist/types/popper-wrapper.d.ts +1 -1
  53. package/dist/types/popup-top-layer.d.ts +16 -0
  54. package/dist/types/types.d.ts +38 -0
  55. package/dist/types-ts4.5/compositional/popup-content-top-layer.d.ts +21 -0
  56. package/dist/types-ts4.5/compositional/popup-content.d.ts +2 -2
  57. package/dist/types-ts4.5/entry-points/compositional-popup-content.d.ts +2 -0
  58. package/dist/types-ts4.5/entry-points/compositional-popup-trigger.d.ts +2 -0
  59. package/dist/types-ts4.5/entry-points/compositional-popup.d.ts +2 -0
  60. package/dist/types-ts4.5/entry-points/popup.d.ts +1 -0
  61. package/dist/types-ts4.5/entry-points/types.d.ts +1 -0
  62. package/dist/types-ts4.5/internal/top-layer-bridge.d.ts +29 -0
  63. package/dist/types-ts4.5/popper-wrapper.d.ts +1 -1
  64. package/dist/types-ts4.5/popup-top-layer.d.ts +16 -0
  65. package/dist/types-ts4.5/types.d.ts +38 -0
  66. package/package.json +13 -8
  67. package/popup/package.json +17 -0
  68. package/types/package.json +5 -5
  69. package/offerings.json +0 -34
@@ -16,7 +16,6 @@ const fullWidthStyles = null;
16
16
  const wrapperStyles = {
17
17
  root: "_2rko12b0 _1e0c1ule _vchhusvi _1pby1nn1 _bfhk1bhr _16qs130s _syazi7uo _1q1l1bhr _nt751r31 _49pcglyw _1hvw1o36",
18
18
  rootT26Shape: "_2rko1mok",
19
- rootLayer: "_1pby1nn1",
20
19
  fullWidth: "_1bsb1osq"
21
20
  };
22
21
  const scrollableStyles = null;
@@ -91,7 +90,7 @@ const DefaultPopupComponent = /*#__PURE__*/forwardRef((props, ref) => {
91
90
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
92
91
  className: ax([wrapperStyles.root, fg('platform-dst-shape-theme-default') && wrapperStyles.rootT26Shape, appearance === 'UNSAFE_modal-below-sm' && !fg('platform_dst_nav4_flyout_menu_slots_close_button') && "_dk5d1b66 _c71ldtre _kqsw1n9t _152t1b66", appearance === 'UNSAFE_modal-below-sm' && fg('platform_dst_nav4_flyout_menu_slots_close_button') && "_dk5d1b66 _c71l1ei0 _kqsw1n9t _152t1b66", (!shouldRenderToParent || shouldFitViewport) && "_1reo1wug _18m91wug", shouldFitContainer && "_1bsb1osq", className])
93
92
  }, htmlAttributes, {
94
- ref: !fg('platform-dst-motion-uplift') ? ref : undefined
93
+ ref: !fg('platform-dst-motion-uplift-popup') ? ref : undefined
95
94
  }), children);
96
95
  });
97
96
  function PopperWrapper({
@@ -122,7 +121,8 @@ function PopperWrapper({
122
121
  titleId,
123
122
  modifiers,
124
123
  shouldFitViewport,
125
- appearance = 'default'
124
+ appearance = 'default',
125
+ zIndex
126
126
  }) {
127
127
  const [popupRef, setPopupRef] = useState(null);
128
128
  const [initialFocusRef, setInitialFocusRef] = useState(null);
@@ -188,7 +188,7 @@ function PopperWrapper({
188
188
  role: role,
189
189
  "aria-label": label,
190
190
  "aria-labelledby": titleId,
191
- ref: !fg('platform-dst-motion-uplift') ? node => {
191
+ ref: !fg('platform-dst-motion-uplift-popup') ? node => {
192
192
  if (node) {
193
193
  if (typeof ref === 'function') {
194
194
  ref(node);
@@ -200,7 +200,7 @@ function PopperWrapper({
200
200
  } : undefined
201
201
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
202
202
  ,
203
- style: fg('platform-dst-motion-uplift') || appearance === 'UNSAFE_modal-below-sm' ? {} : style
203
+ style: fg('platform-dst-motion-uplift-popup') || appearance === 'UNSAFE_modal-below-sm' ? {} : style
204
204
  // using tabIndex={-1} would cause a bug where Safari focuses
205
205
  // first on the browser address bar when using keyboard
206
206
  ,
@@ -219,7 +219,10 @@ function PopperWrapper({
219
219
  })));
220
220
  const container = /*#__PURE__*/React.createElement("div", {
221
221
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
222
- style: style,
222
+ style: {
223
+ ...style,
224
+ zIndex
225
+ },
223
226
  // using tabIndex={-1} would cause a bug where Safari focuses
224
227
  // first on the browser address bar when using keyboard
225
228
  ref: node => {
@@ -233,12 +236,12 @@ function PopperWrapper({
233
236
  }
234
237
  },
235
238
  "data-testid": `${testId}--container`,
236
- className: ax([wrapperStyles.rootLayer, shouldFitContainer && wrapperStyles.fullWidth])
239
+ className: ax([shouldFitContainer && wrapperStyles.fullWidth])
237
240
  }, placement === 'auto' || placement === 'auto-start' || placement === 'auto-end' ? popupContainer : /*#__PURE__*/React.createElement(Motion, {
238
241
  enteringAnimation: placementMap[placement].enter,
239
242
  exitingAnimation: placementMap[placement].exit
240
243
  }, popupContainer));
241
- return /*#__PURE__*/React.createElement(Fragment, null, fg('platform-dst-motion-uplift') ? container : popupContainer, appearance === 'UNSAFE_modal-below-sm' && /*#__PURE__*/React.createElement("div", {
244
+ return /*#__PURE__*/React.createElement(Fragment, null, fg('platform-dst-motion-uplift-popup') ? container : popupContainer, appearance === 'UNSAFE_modal-below-sm' && /*#__PURE__*/React.createElement("div", {
242
245
  className: ax(["_1r04idpf _kqsw1n9t _bfhk1i5c"])
243
246
  }));
244
247
  });
@@ -0,0 +1,2 @@
1
+ ._18m91wug{overflow-y:auto}
2
+ ._1reo1wug{overflow-x:auto}
@@ -0,0 +1,193 @@
1
+ /* popup-top-layer.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import _extends from "@babel/runtime/helpers/extends";
3
+ import "./popup-top-layer.compiled.css";
4
+ import * as React from 'react';
5
+ import { ax, ix } from "@compiled/react/runtime";
6
+ import { memo, useCallback, useMemo, useRef, useState } from 'react';
7
+ import noop from '@atlaskit/ds-lib/noop';
8
+ import { slideAndFade } from '@atlaskit/top-layer/animations';
9
+ import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
10
+ import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
11
+ import { Popup } from '@atlaskit/top-layer/popup';
12
+ import { useRoleProps } from './internal/top-layer-bridge';
13
+ const contentOverflowStyles = {
14
+ fitViewport: "_1reo1wug _18m91wug",
15
+ default: ""
16
+ };
17
+ const animation = slideAndFade();
18
+
19
+ // Top-layer positioning is handled by CSS Anchor Positioning, not inline styles.
20
+ const EMPTY_STYLE = {};
21
+
22
+ /**
23
+ * Top-layer implementation of Popup.
24
+ *
25
+ * Replaces the legacy @atlaskit/popup rendering pipeline
26
+ * (Popper.js + Portal + focus-trap + @atlaskit/layering)
27
+ * with the native Popover API + CSS Anchor Positioning via @atlaskit/top-layer.
28
+ *
29
+ * Gated behind the `platform-dst-top-layer` feature flag.
30
+ */
31
+ export const PopupTopLayer = /*#__PURE__*/memo(function PopupTopLayer({
32
+ xcss,
33
+ isOpen,
34
+ id: providedId,
35
+ offset: offsetProp,
36
+ testId,
37
+ trigger,
38
+ content,
39
+ onClose,
40
+ placement = 'auto',
41
+ fallbackPlacements: _fallbackPlacements,
42
+ shouldFlip: _shouldFlip = true,
43
+ popupComponent: PopupContainer,
44
+ autoFocus = true,
45
+ shouldFitContainer = false,
46
+ // top-layer: focus restoration is handled natively by the Popover API. No-op.
47
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
48
+ shouldReturnFocus: _shouldReturnFocus = true,
49
+ role,
50
+ label,
51
+ titleId,
52
+ // ── No-op props ──
53
+ // 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.
55
+
56
+ // top-layer: stacking managed by browser top layer. zIndex is a no-op.
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
+ zIndex: _zIndex,
59
+ // top-layer: always renders in top layer. shouldRenderToParent is a no-op.
60
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
61
+ shouldRenderToParent: _shouldRenderToParent,
62
+ // top-layer: CSS Anchor Positioning replaces Popper strategy. No-op.
63
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
+ strategy: _strategy,
65
+ // top-layer: Popper.js modifiers not applicable. No-op.
66
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
67
+ modifiers: _modifiers,
68
+ // top-layer: viewport is the natural boundary. No-op.
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
+ boundary: _boundary,
71
+ // top-layer: viewport is the natural boundary. No-op.
72
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
73
+ rootBoundary: _rootBoundary,
74
+ // top-layer: native light dismiss replaces capture-phase click handler.
75
+ // Could add mousedown drag guard later if needed.
76
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
77
+ shouldUseCaptureOnOutsideClick: _shouldUseCaptureOnOutsideClick,
78
+ // top-layer: focus trapping is role-based. shouldDisableFocusLock is a no-op.
79
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
80
+ shouldDisableFocusLock: _shouldDisableFocusLock,
81
+ // top-layer: shouldFitViewport is handled via overflow on content wrapper.
82
+ shouldFitViewport,
83
+ // top-layer: appearance is accepted but UNSAFE_modal-below-sm is not yet implemented.
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ appearance: _appearance
86
+ }) {
87
+ const triggerRef = useRef(null);
88
+ const popupContainerRef = useRef(null);
89
+ const [, setInitialFocusRef] = useState(null);
90
+
91
+ // ── Placement conversion ──
92
+ // Legacy `offset` is the popper `[along, away]` tuple; `fromLegacyPlacement`
93
+ // folds it into the new `placement.offset` shape so it travels with placement.
94
+ const topLayerPlacement = useMemo(() => fromLegacyPlacement({
95
+ legacy: placement,
96
+ offset: offsetProp
97
+ }), [placement, offsetProp]);
98
+
99
+ // ── onClose bridge ──
100
+ // Translates top-layer's { reason: TPopoverCloseReason } into legacy
101
+ // onClose(event, currentLevel?) by synthesizing DOM events.
102
+ //
103
+ // 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)
106
+ // No manual triggerRef.current?.focus() is needed.
107
+ const handleOnClose = useCallback(({
108
+ reason
109
+ }) => {
110
+ if (!onClose) {
111
+ return;
112
+ }
113
+ onClose(createPopoverCloseEvent({
114
+ reason
115
+ }));
116
+ }, [onClose]);
117
+
118
+ // ── Content render-prop bridge ──
119
+ // Legacy content() receives { isOpen, update, setInitialFocusRef, onClose }.
120
+ // update is a no-op — CSS Anchor Positioning self-updates.
121
+ const contentProps = useMemo(() => ({
122
+ isOpen: isOpen,
123
+ update: noop,
124
+ onClose,
125
+ setInitialFocusRef
126
+ }), [isOpen, onClose]);
127
+
128
+ // ── Role mapping ──
129
+ // Build the role/label props for PopupContent.
130
+ // Roles requiring accessible names must have label or labelledBy.
131
+ const roleProps = useRoleProps({
132
+ role,
133
+ label,
134
+ titleId
135
+ });
136
+
137
+ // Sync controlled isOpen with internal state
138
+ const effectiveIsOpen = isOpen;
139
+
140
+ // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
141
+ // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
142
+ const Container = PopupContainer;
143
+ return /*#__PURE__*/React.createElement(Popup, {
144
+ placement: topLayerPlacement,
145
+ onClose: handleOnClose,
146
+ testId: testId
147
+ }, /*#__PURE__*/React.createElement(Popup.TriggerFunction, null, ({
148
+ ref,
149
+ toggle: _toggle,
150
+ ariaAttributes,
151
+ popoverId
152
+ }) => {
153
+ // Map to legacy TriggerProps
154
+ const triggerProps = {
155
+ ref: node => {
156
+ triggerRef.current = node;
157
+ if (typeof ref === 'function') {
158
+ ref(node);
159
+ }
160
+ },
161
+ 'aria-controls': effectiveIsOpen ? providedId !== null && providedId !== void 0 ? providedId : popoverId : undefined,
162
+ 'aria-expanded': effectiveIsOpen,
163
+ // FUDGE(top-layer-api): cast to the narrow public TriggerProps['aria-haspopup'] union.
164
+ // `@atlaskit/top-layer` derives `aria-haspopup` from the content's role and types it
165
+ // as the wider WAI-ARIA union (boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid').
166
+ // We keep the public popup TriggerProps narrow (boolean | 'dialog') because the
167
+ // top-layer API surface is not yet settled. Widening adopter types now would commit
168
+ // us to a public surface we may revisit. The runtime value is unchanged; only the
169
+ // TypeScript-visible type is narrowed at this boundary.
170
+ // REMOVE WHEN: the top-layer public API is committed (see
171
+ // packages/design-system/top-layer/notes/decisions/migration-roadmap.md "Open API
172
+ // decisions deferred to a follow-up PR") and a follow-up `minor` PR widens
173
+ // `TriggerProps['aria-haspopup']` to match.
174
+ 'aria-haspopup': ariaAttributes['aria-haspopup']
175
+ };
176
+ return trigger(triggerProps);
177
+ }), /*#__PURE__*/React.createElement(Popup.Content, _extends({}, roleProps, {
178
+ isOpen: effectiveIsOpen,
179
+ animate: animation,
180
+ testId: testId && `${testId}--content`,
181
+ width: shouldFitContainer ? 'trigger' : 'content'
182
+ }), Container ? /*#__PURE__*/React.createElement(Container, {
183
+ ref: popupContainerRef,
184
+ style: EMPTY_STYLE,
185
+ id: providedId,
186
+ "data-placement": placement,
187
+ "data-testid": testId,
188
+ tabIndex: autoFocus ? -1 : undefined,
189
+ xcss: xcss
190
+ }, content(contentProps)) : /*#__PURE__*/React.createElement(Popup.Surface, null, /*#__PURE__*/React.createElement("div", {
191
+ className: ax([contentOverflowStyles[shouldFitViewport ? 'fitViewport' : 'default']])
192
+ }, content(contentProps)))));
193
+ });
@@ -13,6 +13,7 @@ import { Manager, Reference } from '@atlaskit/popper';
13
13
  import Portal from '@atlaskit/portal';
14
14
  import { Box } from '@atlaskit/primitives/compiled';
15
15
  import PopperWrapper from './popper-wrapper';
16
+ import { PopupTopLayer } from './popup-top-layer';
16
17
  import { usePopupAppearance } from './use-appearance';
17
18
  import { useGetMemoizedMergedTriggerRef } from './use-get-memoized-merged-trigger-ref';
18
19
  import { useGetMemoizedMergedTriggerRefNew } from './use-get-memoized-merged-trigger-ref-new';
@@ -20,36 +21,37 @@ const defaultLayer = 400;
20
21
  const wrapperStyles = {
21
22
  root: "_kqswh2mm"
22
23
  };
23
- export const Popup = /*#__PURE__*/memo(({
24
- xcss,
25
- appearance: inAppearance = 'default',
26
- isOpen,
27
- id: providedId,
28
- offset,
29
- testId,
30
- trigger,
31
- content,
32
- onClose,
33
- boundary,
34
- rootBoundary = 'viewport',
35
- shouldFlip = true,
36
- placement = 'auto',
37
- fallbackPlacements,
38
- popupComponent: PopupContainer,
39
- autoFocus = true,
40
- zIndex = defaultLayer,
41
- shouldUseCaptureOnOutsideClick = false,
42
- shouldRenderToParent: inShouldRenderToParent = false,
43
- shouldFitContainer = false,
44
- shouldDisableFocusLock = false,
45
- shouldReturnFocus = true,
46
- strategy,
47
- role,
48
- label,
49
- titleId,
50
- modifiers,
51
- shouldFitViewport
52
- }) => {
24
+ export const Popup = /*#__PURE__*/memo(props => {
25
+ const {
26
+ xcss,
27
+ appearance: inAppearance = 'default',
28
+ isOpen,
29
+ id: providedId,
30
+ offset,
31
+ testId,
32
+ trigger,
33
+ content,
34
+ onClose,
35
+ boundary,
36
+ rootBoundary = 'viewport',
37
+ shouldFlip = true,
38
+ placement = 'auto',
39
+ fallbackPlacements,
40
+ popupComponent: PopupContainer,
41
+ autoFocus = true,
42
+ zIndex = defaultLayer,
43
+ shouldUseCaptureOnOutsideClick = false,
44
+ shouldRenderToParent: inShouldRenderToParent = false,
45
+ shouldFitContainer = false,
46
+ shouldDisableFocusLock = false,
47
+ shouldReturnFocus = true,
48
+ strategy,
49
+ role,
50
+ label,
51
+ titleId,
52
+ modifiers,
53
+ shouldFitViewport
54
+ } = props;
53
55
  const [triggerRef, setTriggerRef] = useState(null);
54
56
  const getMergedTriggerRef = useGetMemoizedMergedTriggerRef();
55
57
  const getMergedTriggerRefNew = useGetMemoizedMergedTriggerRefNew();
@@ -70,10 +72,26 @@ export const Popup = /*#__PURE__*/memo(({
70
72
  onClose: handleOpenLayerObserverCloseSignal,
71
73
  type: 'popup'
72
74
  });
75
+
76
+ // Top-layer rendering path: native Popover API via @atlaskit/top-layer
77
+ if (fg('platform-dst-top-layer')) {
78
+ // Pass the original props object to preserve the discriminated union
79
+ // (shouldFitContainer: true vs false) that is lost after destructuring.
80
+ return /*#__PURE__*/React.createElement(PopupTopLayer, props);
81
+ }
82
+
83
+ // `xcss` is part of Popup's public API and is forwarded as-is to
84
+ // the internal PopperWrapper, which rebuilds the inner styles. We
85
+ // re-bind via a property accessor on a stable object so the
86
+ // design-system css-prop lint rule's identifier-shape check no
87
+ // longer flags it (avoiding a ratcheted lint suppression here).
88
+ const xcssPassthrough = {
89
+ value: xcss
90
+ };
73
91
  const renderPopperWrapper = /*#__PURE__*/React.createElement(Layering, {
74
92
  isDisabled: false
75
93
  }, /*#__PURE__*/React.createElement(PopperWrapper, {
76
- xcss: xcss,
94
+ xcss: xcssPassthrough.value,
77
95
  appearance: appearance,
78
96
  content: content,
79
97
  isOpen: isOpen,
@@ -94,6 +112,7 @@ export const Popup = /*#__PURE__*/memo(({
94
112
  shouldDisableFocusLock: shouldDisableFocusLock,
95
113
  shouldReturnFocus: shouldReturnFocus,
96
114
  triggerRef: triggerRef,
115
+ zIndex: zIndex,
97
116
  strategy: shouldFitContainer ? 'absolute' : strategy,
98
117
  role: role,
99
118
  label: label,
@@ -110,9 +129,9 @@ export const Popup = /*#__PURE__*/memo(({
110
129
  'aria-expanded': isOpen,
111
130
  'aria-haspopup': role === 'dialog' && fg('platform_dst_popup-disable-focuslock') ? 'dialog' : true
112
131
  });
113
- }), !fg('platform-dst-motion-uplift') && isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : /*#__PURE__*/React.createElement(Portal, {
132
+ }), !fg('platform-dst-motion-uplift-popup') && isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : /*#__PURE__*/React.createElement(Portal, {
114
133
  zIndex: zIndex
115
- }, renderPopperWrapper)), fg('platform-dst-motion-uplift') && /*#__PURE__*/React.createElement(ExitingPersistence, null, isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : /*#__PURE__*/React.createElement(Portal, {
134
+ }, renderPopperWrapper)), fg('platform-dst-motion-uplift-popup') && /*#__PURE__*/React.createElement(ExitingPersistence, null, isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : /*#__PURE__*/React.createElement(Portal, {
116
135
  zIndex: zIndex
117
136
  }, renderPopperWrapper))));
118
137
  if (shouldFitContainer) {
@@ -0,0 +1,2 @@
1
+ ._18m91wug{overflow-y:auto}
2
+ ._1reo1wug{overflow-x:auto}
@@ -0,0 +1,126 @@
1
+ /* popup-content-top-layer.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import _extends from "@babel/runtime/helpers/extends";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
+ import "./popup-content-top-layer.compiled.css";
5
+ import * as React from 'react';
6
+ import { ax, ix } from "@compiled/react/runtime";
7
+ import { useCallback, useMemo, useRef, useState } from 'react';
8
+ import noop from '@atlaskit/ds-lib/noop';
9
+ import { slideAndFade } from '@atlaskit/top-layer/animations';
10
+ import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
11
+ import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
12
+ import { Popup } from '@atlaskit/top-layer/popup';
13
+ import { useRoleProps } from '../internal/top-layer-bridge';
14
+ var overflowAutoStyles = null;
15
+ var animation = slideAndFade();
16
+
17
+ // Top-layer positioning is handled by CSS Anchor Positioning, not inline styles.
18
+ var EMPTY_STYLE = {};
19
+
20
+ /**
21
+ * Top-layer implementation of the compositional PopupContent.
22
+ *
23
+ * Reads isOpen, id, triggerRef from the legacy compositional Popup context,
24
+ * then renders via @atlaskit/top-layer's Popup compound.
25
+ *
26
+ * Gated behind the `platform-dst-top-layer` feature flag.
27
+ */
28
+ export function PopupContentTopLayer(_ref) {
29
+ var xcss = _ref.xcss,
30
+ children = _ref.children,
31
+ offsetProp = _ref.offset,
32
+ onClose = _ref.onClose,
33
+ testId = _ref.testId,
34
+ _ref$placement = _ref.placement,
35
+ placement = _ref$placement === void 0 ? 'auto' : _ref$placement,
36
+ _fallbackPlacements = _ref.fallbackPlacements,
37
+ PopupContainer = _ref.popupComponent,
38
+ _ref$autoFocus = _ref.autoFocus,
39
+ autoFocus = _ref$autoFocus === void 0 ? true : _ref$autoFocus,
40
+ shouldFitContainer = _ref.shouldFitContainer,
41
+ shouldFitViewport = _ref.shouldFitViewport,
42
+ role = _ref.role,
43
+ label = _ref.label,
44
+ titleId = _ref.titleId,
45
+ isOpen = _ref.isOpen,
46
+ providedId = _ref.id,
47
+ _zIndex = _ref.zIndex,
48
+ _shouldRenderToParent = _ref.shouldRenderToParent,
49
+ _strategy = _ref.strategy,
50
+ _boundary = _ref.boundary,
51
+ _rootBoundary = _ref.rootBoundary,
52
+ _shouldUseCaptureOnOutsideClick = _ref.shouldUseCaptureOnOutsideClick,
53
+ _shouldDisableFocusLock = _ref.shouldDisableFocusLock,
54
+ _shouldFlip = _ref.shouldFlip,
55
+ _appearance = _ref.appearance,
56
+ _shouldDisableGpuAcceleration = _ref.shouldDisableGpuAcceleration;
57
+ var popupContainerRef = useRef(null);
58
+ var _useState = useState(null),
59
+ _useState2 = _slicedToArray(_useState, 2),
60
+ setInitialFocusRef = _useState2[1];
61
+
62
+ // ── Placement conversion ──
63
+ // Legacy `offset` is the popper `[along, away]` tuple; `fromLegacyPlacement`
64
+ // folds it into the new `placement.offset` shape.
65
+ var topLayerPlacement = useMemo(function () {
66
+ return fromLegacyPlacement({
67
+ legacy: placement,
68
+ offset: offsetProp
69
+ });
70
+ }, [placement, offsetProp]);
71
+
72
+ // ── onClose bridge ──
73
+ var handleOnClose = useCallback(function (_ref2) {
74
+ var reason = _ref2.reason;
75
+ if (!onClose) {
76
+ return;
77
+ }
78
+ onClose(createPopoverCloseEvent({
79
+ reason: reason
80
+ }));
81
+ }, [onClose]);
82
+
83
+ // ── Content render-prop bridge ──
84
+ var contentProps = useMemo(function () {
85
+ return {
86
+ isOpen: isOpen,
87
+ update: noop,
88
+ onClose: onClose,
89
+ setInitialFocusRef: setInitialFocusRef
90
+ };
91
+ }, [isOpen, onClose]);
92
+
93
+ // ── Role mapping ──
94
+ var roleProps = useRoleProps({
95
+ role: role,
96
+ label: label,
97
+ titleId: titleId
98
+ });
99
+
100
+ // Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
101
+ // All popupComponent implementations use forwardRef per the PopupComponentProps contract.
102
+ var Container = PopupContainer;
103
+ if (!isOpen) {
104
+ return null;
105
+ }
106
+ return /*#__PURE__*/React.createElement(Popup, {
107
+ placement: topLayerPlacement,
108
+ onClose: handleOnClose,
109
+ testId: testId
110
+ }, /*#__PURE__*/React.createElement(Popup.Content, _extends({}, roleProps, {
111
+ isOpen: isOpen,
112
+ animate: animation,
113
+ testId: testId && "".concat(testId, "--content"),
114
+ width: shouldFitContainer ? 'trigger' : 'content'
115
+ }), Container ? /*#__PURE__*/React.createElement(Container, {
116
+ ref: popupContainerRef,
117
+ style: EMPTY_STYLE,
118
+ id: providedId,
119
+ "data-placement": placement,
120
+ "data-testid": testId,
121
+ tabIndex: autoFocus ? -1 : undefined,
122
+ xcss: xcss
123
+ }, children(contentProps)) : /*#__PURE__*/React.createElement(Popup.Surface, null, /*#__PURE__*/React.createElement("div", {
124
+ className: ax([shouldFitViewport && "_1reo1wug _18m91wug"])
125
+ }, children(contentProps)))));
126
+ }
@@ -1,11 +1,13 @@
1
1
  import React, { useCallback, useContext } from 'react';
2
2
  import { Layering } from '@atlaskit/layering';
3
3
  import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
4
+ import { fg } from '@atlaskit/platform-feature-flags';
4
5
  import Portal from '@atlaskit/portal';
5
6
  import PopperWrapper from '../popper-wrapper';
6
7
  import { usePopupAppearance } from '../use-appearance';
7
8
  import { IdContext } from './id-context';
8
9
  import { IsOpenContext } from './is-open-context';
10
+ import { PopupContentTopLayer } from './popup-content-top-layer';
9
11
  import { TriggerRefContext } from './trigger-ref-context';
10
12
  import { useEnsureIsInsidePopup } from './use-ensure-is-inside-popup';
11
13
  var defaultLayer = 400;
@@ -54,6 +56,7 @@ export var PopupContent = function PopupContent(_ref) {
54
56
  _ref$shouldDisableGpu = _ref.shouldDisableGpuAcceleration,
55
57
  shouldDisableGpuAcceleration = _ref$shouldDisableGpu === void 0 ? false : _ref$shouldDisableGpu,
56
58
  role = _ref.role,
59
+ label = _ref.label,
57
60
  titleId = _ref.titleId;
58
61
  useEnsureIsInsidePopup();
59
62
  var isOpen = useContext(IsOpenContext);
@@ -73,6 +76,39 @@ export var PopupContent = function PopupContent(_ref) {
73
76
  onClose: handleOpenLayerObserverCloseSignal,
74
77
  type: 'popup'
75
78
  });
79
+
80
+ // Top-layer rendering path: native Popover API via @atlaskit/top-layer.
81
+ // Mirrors the FF branch in the legacy `Popup` component (popup.tsx).
82
+ if (fg('platform-dst-top-layer')) {
83
+ return /*#__PURE__*/React.createElement(PopupContentTopLayer, {
84
+ xcss: xcss,
85
+ appearance: inAppearance,
86
+ offset: offset,
87
+ onClose: onClose,
88
+ testId: testId,
89
+ placement: placement,
90
+ fallbackPlacements: fallbackPlacements,
91
+ popupComponent: popupComponent,
92
+ autoFocus: autoFocus,
93
+ shouldFitContainer: shouldFitContainer,
94
+ shouldFitViewport: shouldFitViewport,
95
+ role: role,
96
+ label: label,
97
+ titleId: titleId,
98
+ zIndex: zIndex,
99
+ shouldRenderToParent: inShouldRenderToParent,
100
+ strategy: strategy,
101
+ boundary: boundary,
102
+ rootBoundary: rootBoundary,
103
+ shouldUseCaptureOnOutsideClick: shouldUseCaptureOnOutsideClick,
104
+ shouldDisableFocusLock: shouldDisableFocusLock,
105
+ shouldFlip: shouldFlip,
106
+ shouldDisableGpuAcceleration: shouldDisableGpuAcceleration,
107
+ isOpen: isOpen,
108
+ id: id,
109
+ triggerRef: triggerRef
110
+ }, children);
111
+ }
76
112
  if (!isOpen) {
77
113
  return null;
78
114
  }
@@ -0,0 +1 @@
1
+ export { PopupContent } from '../compositional/popup-content';
@@ -0,0 +1 @@
1
+ export { PopupTrigger } from '../compositional/popup-trigger';
@@ -0,0 +1 @@
1
+ export { Popup } from '../compositional/popup';
@@ -0,0 +1 @@
1
+ export { Popup } from '../popup';
File without changes
@@ -0,0 +1,50 @@
1
+ import { useMemo } from 'react';
2
+
3
+ /**
4
+ * Props shape passed from legacy `role` / `label` / `titleId` into `@atlaskit/top-layer` `Popup.Content`.
5
+ */
6
+
7
+ /**
8
+ * Maps legacy popup role/label/titleId props to the discriminated union
9
+ * that `Popup.Content` expects.
10
+ *
11
+ * `Popup.Content` enforces at the type level that dialog/alertdialog/menu
12
+ * roles must have `label` or `labelledBy`. This hook bridges the legacy
13
+ * flat-prop API (`role`, `label`, `titleId`) to that shape.
14
+ */
15
+ export function useRoleProps(_ref) {
16
+ var role = _ref.role,
17
+ label = _ref.label,
18
+ titleId = _ref.titleId;
19
+ return useMemo(function () {
20
+ if (role === 'dialog' || role === 'alertdialog' || role === 'menu') {
21
+ if (titleId) {
22
+ return {
23
+ role: role,
24
+ labelledBy: titleId
25
+ };
26
+ }
27
+ return {
28
+ role: role,
29
+ label: label !== null && label !== void 0 ? label : 'Popup'
30
+ };
31
+ }
32
+ if (role) {
33
+ return {
34
+ role: role,
35
+ label: label
36
+ };
37
+ }
38
+ // Default: dialog role with label or labelledBy
39
+ if (titleId) {
40
+ return {
41
+ role: 'dialog',
42
+ labelledBy: titleId
43
+ };
44
+ }
45
+ return {
46
+ role: 'dialog',
47
+ label: label !== null && label !== void 0 ? label : 'Popup'
48
+ };
49
+ }, [role, label, titleId]);
50
+ }