@atlaskit/popup 6.3.3 → 6.3.5
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 +22 -0
- package/__tests__/playwright/fit-viewport.spec.tsx +225 -0
- package/dist/cjs/compositional/popup-content-top-layer.js +30 -19
- package/dist/cjs/compositional/popup.js +1 -1
- package/dist/cjs/compositional/trigger-ref-object-context.js +2 -2
- package/dist/cjs/internal/get-popup-axis-sizes.js +42 -0
- package/dist/cjs/popper-wrapper.js +1 -1
- package/dist/cjs/popup-top-layer.js +28 -20
- package/dist/cjs/popup.js +1 -1
- package/dist/cjs/use-appearance.js +11 -3
- package/dist/es2019/compositional/popup-content-top-layer.js +27 -18
- package/dist/es2019/compositional/popup.js +1 -1
- package/dist/es2019/compositional/trigger-ref-object-context.js +2 -2
- package/dist/es2019/internal/get-popup-axis-sizes.js +37 -0
- package/dist/es2019/popper-wrapper.js +1 -1
- package/dist/es2019/popup-top-layer.js +32 -21
- package/dist/es2019/popup.js +1 -1
- package/dist/es2019/use-appearance.js +11 -3
- package/dist/esm/compositional/popup-content-top-layer.js +30 -18
- package/dist/esm/compositional/popup.js +1 -1
- package/dist/esm/compositional/trigger-ref-object-context.js +2 -2
- package/dist/esm/internal/get-popup-axis-sizes.js +36 -0
- package/dist/esm/popper-wrapper.js +1 -1
- package/dist/esm/popup-top-layer.js +28 -20
- package/dist/esm/popup.js +1 -1
- package/dist/esm/use-appearance.js +11 -3
- package/dist/types/compositional/trigger-ref-object-context.d.ts +2 -2
- package/dist/types/internal/get-popup-axis-sizes.d.ts +25 -0
- package/dist/types/types.d.ts +30 -0
- package/package.json +9 -4
- package/dist/cjs/compositional/popup-content-top-layer.compiled.css +0 -2
- package/dist/cjs/popup-top-layer.compiled.css +0 -2
- package/dist/es2019/compositional/popup-content-top-layer.compiled.css +0 -2
- package/dist/es2019/popup-top-layer.compiled.css +0 -2
- package/dist/esm/compositional/popup-content-top-layer.compiled.css +0 -2
- package/dist/esm/popup-top-layer.compiled.css +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atlaskit/popup
|
|
2
2
|
|
|
3
|
+
## 6.3.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`a7ec100f7d560`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a7ec100f7d560) -
|
|
8
|
+
Fixed `shouldFitViewport` doing nothing on the top-layer code path, and forwarded it to a custom
|
|
9
|
+
`popupComponent` as on the legacy path. `xcss` on `<Popup>` is now applied on the top-layer path
|
|
10
|
+
without a custom `popupComponent`; it was silently dropped there. Behind the
|
|
11
|
+
`platform-dst-top-layer` gate; legacy (flag-off) behaviour is unchanged.
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
|
|
14
|
+
## 6.3.4
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [`345efa8128cb2`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/345efa8128cb2) -
|
|
19
|
+
Behind platform_dst_popup_media_query_perf_fix Popups with the default appearance no longer cause
|
|
20
|
+
state changes across the below.sm viewport media query Popups with the UNSAFE_modal-below-sm
|
|
21
|
+
appearance (modal) still cause the state change This was done to fix popup heavy pages stuttering
|
|
22
|
+
on resizing, as the state change was causing unnecessary re-renders
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
|
|
3
25
|
## 6.3.3
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/* eslint-disable testing-library/prefer-screen-queries */
|
|
2
|
+
import invariant from 'tiny-invariant';
|
|
3
|
+
|
|
4
|
+
import { expect, type Page, test } from '@af/integration-testing';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `Popup`'s `shouldFitViewport`, asserted as geometry on both code paths. On the
|
|
8
|
+
* top-layer path the prop did nothing: its whole implementation was
|
|
9
|
+
* `overflow: auto` on a wrapper inside a `PopoverSurface` that already set it,
|
|
10
|
+
* and `overflow: auto` with no cap is inert.
|
|
11
|
+
*
|
|
12
|
+
* The assertions are path-agnostic, so they double as the legacy/top-layer parity
|
|
13
|
+
* check: a scroll container exists, is within the viewport, actually scrolls, and
|
|
14
|
+
* can bring the control at the bottom of the content on screen. The first three
|
|
15
|
+
* are what make this more than a reachability test, which a popover clamped to a
|
|
16
|
+
* 53px letterbox also passes.
|
|
17
|
+
*
|
|
18
|
+
* The flag is toggled with a bare `featureFlag` key and omitted entirely for
|
|
19
|
+
* flag-off, which is the only form the examples page understands.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const TOP_LAYER_FLAG = 'platform-dst-top-layer';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sub-pixel layout differences.
|
|
26
|
+
*/
|
|
27
|
+
const TOLERANCE = 1;
|
|
28
|
+
|
|
29
|
+
const VIEWPORT = { width: 400, height: 400 };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The nearest ancestor of the content whose `overflow` is scrollable. Found by
|
|
33
|
+
* walking up rather than by `testId`, because the two code paths put it on
|
|
34
|
+
* different elements and the point is that SOMETHING scrolls.
|
|
35
|
+
*/
|
|
36
|
+
async function readScrollContainer({ page }: { page: Page }): Promise<{
|
|
37
|
+
top: number;
|
|
38
|
+
bottom: number;
|
|
39
|
+
clientHeight: number;
|
|
40
|
+
scrollHeight: number;
|
|
41
|
+
viewportHeight: number;
|
|
42
|
+
} | null> {
|
|
43
|
+
return page.evaluate(() => {
|
|
44
|
+
const content = document.querySelector('[data-testid="fit-popup-body"]');
|
|
45
|
+
let element = content?.parentElement ?? null;
|
|
46
|
+
|
|
47
|
+
while (element) {
|
|
48
|
+
const overflowY = window.getComputedStyle(element).overflowY;
|
|
49
|
+
if (overflowY === 'auto' || overflowY === 'scroll') {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
element = element.parentElement;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!element) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const rect = element.getBoundingClientRect();
|
|
60
|
+
return {
|
|
61
|
+
top: rect.top,
|
|
62
|
+
bottom: rect.bottom,
|
|
63
|
+
clientHeight: element.clientHeight,
|
|
64
|
+
scrollHeight: element.scrollHeight,
|
|
65
|
+
viewportHeight: window.innerHeight,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function openFixture({
|
|
71
|
+
page,
|
|
72
|
+
params,
|
|
73
|
+
isTopLayer,
|
|
74
|
+
}: {
|
|
75
|
+
page: Page;
|
|
76
|
+
params: { [key: string]: string };
|
|
77
|
+
isTopLayer: boolean;
|
|
78
|
+
}): Promise<void> {
|
|
79
|
+
await page.setViewportSize(VIEWPORT);
|
|
80
|
+
await page.visitExample<typeof import('../../examples/98-testing-fit-viewport.tsx')>(
|
|
81
|
+
'design-system',
|
|
82
|
+
'popup',
|
|
83
|
+
'testing-fit-viewport',
|
|
84
|
+
isTopLayer ? { ...params, featureFlag: TOP_LAYER_FLAG } : params,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await page.getByTestId('fit-popup-trigger').click();
|
|
88
|
+
await expect(page.getByTestId('fit-popup-body')).toBeVisible();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The trigger sits 300px down a 400px viewport, so there are only ~80px below it
|
|
93
|
+
* and the 400px-tall content cannot possibly fit on either side unclamped.
|
|
94
|
+
*/
|
|
95
|
+
const CRAMPED = { triggerBlockStart: '300', contentBlockSize: '400' };
|
|
96
|
+
|
|
97
|
+
[
|
|
98
|
+
{ name: 'top-layer', isTopLayer: true },
|
|
99
|
+
{ name: 'legacy', isTopLayer: false },
|
|
100
|
+
].forEach(({ name, isTopLayer }) => {
|
|
101
|
+
test(`${name}: a fitting popup is capped, on screen, and scrolls`, async ({ page }) => {
|
|
102
|
+
await openFixture({ page, params: { ...CRAMPED, fit: 'true' }, isTopLayer });
|
|
103
|
+
|
|
104
|
+
await expect(async () => {
|
|
105
|
+
const scroller = await readScrollContainer({ page });
|
|
106
|
+
invariant(scroller, 'the popup should have a scroll container');
|
|
107
|
+
|
|
108
|
+
expect(scroller.top).toBeGreaterThanOrEqual(-TOLERANCE);
|
|
109
|
+
expect(scroller.bottom).toBeLessThanOrEqual(scroller.viewportHeight + TOLERANCE);
|
|
110
|
+
expect(scroller.scrollHeight).toBeGreaterThan(scroller.clientHeight);
|
|
111
|
+
}).toPass({ timeout: 10_000, intervals: [50, 100, 200, 500] });
|
|
112
|
+
|
|
113
|
+
// The reported symptom: the control at the bottom of the content has to be
|
|
114
|
+
// reachable. Scrolling the popup's own container must bring it on screen.
|
|
115
|
+
await page.getByTestId('fit-popup-footer-action').scrollIntoViewIfNeeded();
|
|
116
|
+
await expect(page.getByTestId('fit-popup-footer-action')).toBeInViewport();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('top-layer: the cap reaches a custom popupComponent', async ({ page }) => {
|
|
121
|
+
// The consumer's element is the direct child of the popover host, so the cap
|
|
122
|
+
// has to reach it through the flex context. The container also keys its own
|
|
123
|
+
// `overflow: auto` off `shouldFitViewport`, which the top-layer path did not
|
|
124
|
+
// forward, so this covers both halves.
|
|
125
|
+
await openFixture({
|
|
126
|
+
page,
|
|
127
|
+
params: { ...CRAMPED, fit: 'true', container: 'custom' },
|
|
128
|
+
isTopLayer: true,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await expect(async () => {
|
|
132
|
+
const container = await page.evaluate(() => {
|
|
133
|
+
const element = document.querySelector('[data-testid="fit-popup-container"]');
|
|
134
|
+
if (!element) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const rect = element.getBoundingClientRect();
|
|
138
|
+
return {
|
|
139
|
+
top: rect.top,
|
|
140
|
+
bottom: rect.bottom,
|
|
141
|
+
clientHeight: element.clientHeight,
|
|
142
|
+
scrollHeight: element.scrollHeight,
|
|
143
|
+
overflowY: window.getComputedStyle(element).overflowY,
|
|
144
|
+
viewportHeight: window.innerHeight,
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
invariant(container, 'the custom popup container should be rendered');
|
|
149
|
+
|
|
150
|
+
// The prop reached the container.
|
|
151
|
+
expect(container.overflowY).toBe('auto');
|
|
152
|
+
// The host cap reached the container.
|
|
153
|
+
expect(container.bottom).toBeLessThanOrEqual(container.viewportHeight + TOLERANCE);
|
|
154
|
+
expect(container.top).toBeGreaterThanOrEqual(-TOLERANCE);
|
|
155
|
+
expect(container.scrollHeight).toBeGreaterThan(container.clientHeight);
|
|
156
|
+
}).toPass({ timeout: 10_000, intervals: [50, 100, 200, 500] });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* `xcss` on `<Popup>` has to reach the DOM on both paths. Legacy applies it to the
|
|
161
|
+
* container element; the top-layer path, with no custom `popupComponent`, applies it
|
|
162
|
+
* to a wrapper inside `PopoverSurface`. Either way it is the content's PARENT, so
|
|
163
|
+
* the assertion is path-agnostic. The content is 160px wide by its own styles, so
|
|
164
|
+
* only the consumer's `width: 280px` can make that parent 280px wide. Before the
|
|
165
|
+
* wrapper the top-layer path dropped the prop, and four call sites lost their width.
|
|
166
|
+
*/
|
|
167
|
+
[
|
|
168
|
+
{ name: 'top-layer', isTopLayer: true },
|
|
169
|
+
{ name: 'legacy', isTopLayer: false },
|
|
170
|
+
].forEach(({ name, isTopLayer }) => {
|
|
171
|
+
test(`${name}: an xcss width on <Popup> reaches the rendered DOM`, async ({ page }) => {
|
|
172
|
+
await openFixture({
|
|
173
|
+
page,
|
|
174
|
+
params: { triggerBlockStart: '100', contentBlockSize: '100', width: 'fixed' },
|
|
175
|
+
isTopLayer,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await expect(async () => {
|
|
179
|
+
const parentWidth = await page.evaluate(() => {
|
|
180
|
+
const content = document.querySelector('[data-testid="fit-popup-body"]');
|
|
181
|
+
return content?.parentElement?.getBoundingClientRect().width ?? null;
|
|
182
|
+
});
|
|
183
|
+
invariant(parentWidth !== null, 'the popup content should have a parent element');
|
|
184
|
+
|
|
185
|
+
expect(Math.abs(parentWidth - 280)).toBeLessThanOrEqual(TOLERANCE);
|
|
186
|
+
}).toPass({ timeout: 10_000, intervals: [50, 100, 200, 500] });
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The control: fitting stays OPT-IN, since fitting by default would be a
|
|
192
|
+
* placement change for ~53 call sites. Two ways to be opted out, failing to two
|
|
193
|
+
* different regressions: an explicit `false` fails if the adapter stops reading
|
|
194
|
+
* the prop, and the prop left OFF additionally fails if the
|
|
195
|
+
* `shouldFitViewport = false` default in `popup-top-layer.tsx` is flipped.
|
|
196
|
+
*
|
|
197
|
+
* Measured on the SCROLL CONTAINER, because `fit-popup-body` carries a hard 400px
|
|
198
|
+
* `blockSize` and `getBoundingClientRect` ignores an ancestor's `overflow`. The
|
|
199
|
+
* container is capped and scrolls in BOTH states, the viewport backstop being
|
|
200
|
+
* unconditional; what fitting adds is the cell cap and the flip floor.
|
|
201
|
+
*/
|
|
202
|
+
const OPT_OUT_CASES = [
|
|
203
|
+
{ name: 'an explicit shouldFitViewport={false}', fit: 'false' },
|
|
204
|
+
{ name: 'the prop left off, so the adapter default decides', fit: undefined },
|
|
205
|
+
] as const;
|
|
206
|
+
|
|
207
|
+
OPT_OUT_CASES.forEach(({ name, fit }) => {
|
|
208
|
+
test(`top-layer: not fitted to the space beside the trigger, with ${name}`, async ({ page }) => {
|
|
209
|
+
await openFixture({
|
|
210
|
+
page,
|
|
211
|
+
params: fit === undefined ? { ...CRAMPED } : { ...CRAMPED, fit },
|
|
212
|
+
isTopLayer: true,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
await expect(async () => {
|
|
216
|
+
const scroller = await readScrollContainer({ page });
|
|
217
|
+
invariant(scroller, 'the popup should have a scroll container');
|
|
218
|
+
|
|
219
|
+
// It starts on screen, next to the trigger, and runs off the bottom: a
|
|
220
|
+
// popup left where it was anchored rather than one moved or clamped.
|
|
221
|
+
expect(scroller.top).toBeLessThan(scroller.viewportHeight);
|
|
222
|
+
expect(scroller.bottom).toBeGreaterThan(scroller.viewportHeight + TOLERANCE);
|
|
223
|
+
}).toPass({ timeout: 10_000, intervals: [50, 100, 200, 500] });
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* popup-content-top-layer.tsx generated by @compiled/babel-plugin
|
|
1
|
+
/* popup-content-top-layer.tsx generated by @compiled/babel-plugin v3.0.1 */
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
@@ -7,23 +7,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
value: true
|
|
8
8
|
});
|
|
9
9
|
exports.PopupContentTopLayer = PopupContentTopLayer;
|
|
10
|
-
require("./popup-content-top-layer.compiled.css");
|
|
11
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
12
11
|
var React = _react;
|
|
13
|
-
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
14
12
|
var _runtime = require("@compiled/react/runtime");
|
|
13
|
+
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
14
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
15
15
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
16
16
|
var _index = require("@atlaskit/top-layer/placement-map/index");
|
|
17
17
|
var _createCloseEvent = require("@atlaskit/top-layer/popover/create-close-event");
|
|
18
18
|
var _popover = require("@atlaskit/top-layer/popover/popover");
|
|
19
19
|
var _popoverSurface = require("@atlaskit/top-layer/popover-surface");
|
|
20
|
-
var
|
|
21
|
-
var
|
|
20
|
+
var _useAnchoredPopover = require("@atlaskit/top-layer/use-anchored-popover");
|
|
21
|
+
var _getPopupAxisSizes = require("../internal/get-popup-axis-sizes");
|
|
22
22
|
var _topLayerBridge = require("../internal/top-layer-bridge");
|
|
23
23
|
var _triggerRefObjectContext = require("./trigger-ref-object-context");
|
|
24
24
|
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); }
|
|
25
|
-
var
|
|
26
|
-
|
|
25
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
26
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
27
27
|
// Top-layer positioning is handled by CSS Anchor Positioning, not inline styles.
|
|
28
28
|
var EMPTY_STYLE = {};
|
|
29
29
|
|
|
@@ -47,8 +47,10 @@ function PopupContentTopLayer(_ref) {
|
|
|
47
47
|
PopupContainer = _ref.popupComponent,
|
|
48
48
|
_ref$autoFocus = _ref.autoFocus,
|
|
49
49
|
autoFocus = _ref$autoFocus === void 0 ? true : _ref$autoFocus,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
_ref$shouldFitContain = _ref.shouldFitContainer,
|
|
51
|
+
shouldFitContainer = _ref$shouldFitContain === void 0 ? false : _ref$shouldFitContain,
|
|
52
|
+
_ref$shouldFitViewpor = _ref.shouldFitViewport,
|
|
53
|
+
shouldFitViewport = _ref$shouldFitViewpor === void 0 ? false : _ref$shouldFitViewpor,
|
|
52
54
|
role = _ref.role,
|
|
53
55
|
label = _ref.label,
|
|
54
56
|
titleId = _ref.titleId,
|
|
@@ -168,18 +170,15 @@ function PopupContentTopLayer(_ref) {
|
|
|
168
170
|
|
|
169
171
|
// `isOpen` is included so the anchor positioning effect re-runs when
|
|
170
172
|
// the Popover host element is unmounted/remounted across open cycles.
|
|
171
|
-
(0,
|
|
173
|
+
(0, _useAnchoredPopover.useAnchoredPopover)(_objectSpread({
|
|
172
174
|
anchorRef: anchorRef,
|
|
173
175
|
popoverRef: popoverRef,
|
|
174
176
|
placement: topLayerPlacement,
|
|
175
177
|
isOpen: isOpen
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
anchorRef: anchorRef,
|
|
181
|
-
isOpen: isOpen
|
|
182
|
-
});
|
|
178
|
+
}, (0, _getPopupAxisSizes.getPopupAxisSizes)({
|
|
179
|
+
shouldFitContainer: shouldFitContainer,
|
|
180
|
+
shouldFitViewport: shouldFitViewport
|
|
181
|
+
})));
|
|
183
182
|
|
|
184
183
|
// Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
|
|
185
184
|
// All popupComponent implementations use forwardRef per the PopupComponentProps contract.
|
|
@@ -199,8 +198,20 @@ function PopupContentTopLayer(_ref) {
|
|
|
199
198
|
"data-testid": testId,
|
|
200
199
|
tabIndex: autoFocus ? -1 : undefined,
|
|
201
200
|
xcss: xcss
|
|
202
|
-
|
|
201
|
+
// Forwarded because several in-tree containers key their own
|
|
202
|
+
// `overflow: auto` branch off it.
|
|
203
|
+
,
|
|
204
|
+
shouldFitViewport: shouldFitViewport
|
|
205
|
+
}, children(contentProps)) :
|
|
206
|
+
/*#__PURE__*/
|
|
207
|
+
// `PopoverSurface` owns `overflow: auto` and the `box-shadow` on the
|
|
208
|
+
// same element, so it is the scroll container. The wrapper exists only
|
|
209
|
+
// to carry the consumer's `xcss`: `PopoverSurface` exposes no
|
|
210
|
+
// `className` or `xcss` by design, so `xcss` lands on a CHILD of the
|
|
211
|
+
// surface. `width` works there; surface-level declarations apply one
|
|
212
|
+
// level in. Same shape as `../popup-top-layer.tsx`.
|
|
213
|
+
React.createElement(_popoverSurface.PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
|
|
203
214
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
204
|
-
className:
|
|
215
|
+
className: xcss
|
|
205
216
|
}, children(contentProps))));
|
|
206
217
|
}
|
|
@@ -52,7 +52,7 @@ var Popup = exports.Popup = function Popup(_ref) {
|
|
|
52
52
|
// When the top-layer flag is on, we maintain a stable RefObject for the
|
|
53
53
|
// trigger element, provided through TriggerRefObjectContext. Unlike the
|
|
54
54
|
// state-based triggerRef (which causes a re-render when the trigger mounts),
|
|
55
|
-
// this ref has a stable identity so
|
|
55
|
+
// this ref has a stable identity so useAnchoredPopover's useLayoutEffect dep
|
|
56
56
|
// array never changes — the effect runs once on mount when the trigger element
|
|
57
57
|
// is already populated. PopupTrigger sets this ref directly instead of calling
|
|
58
58
|
// the state setter, avoiding an unnecessary re-render.
|
|
@@ -12,8 +12,8 @@ var _react = require("react");
|
|
|
12
12
|
* for the top-layer path. Unlike `TriggerRefContext` (which provides
|
|
13
13
|
* `HTMLElement | null` as React state and causes a re-render when the trigger
|
|
14
14
|
* mounts), this context provides a ref object whose identity is stable across
|
|
15
|
-
* renders — allowing `
|
|
16
|
-
*
|
|
15
|
+
* renders — allowing `useAnchoredPopover` to receive a stable anchor ref without
|
|
16
|
+
* needing to create a new ref wrapper on every `triggerRef` state change.
|
|
17
17
|
*/
|
|
18
18
|
var TriggerRefObjectContext = exports.TriggerRefObjectContext = /*#__PURE__*/(0, _react.createContext)({
|
|
19
19
|
current: null
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getPopupAxisSizes = getPopupAxisSizes;
|
|
7
|
+
/**
|
|
8
|
+
* Maps the legacy `shouldFitContainer` / `shouldFitViewport` pair onto
|
|
9
|
+
* `useAnchoredPopover`'s per-axis sizes. Shared by the standard and the
|
|
10
|
+
* compositional adapter so the two cannot drift.
|
|
11
|
+
*
|
|
12
|
+
* Both props can be set together while `inlineSize` takes one value, so
|
|
13
|
+
* `shouldFitContainer` wins the inline axis, being the more specific request.
|
|
14
|
+
* Fitting then applies to the block axis only, and still turns the
|
|
15
|
+
* placement-axis floor on, so a cramped popup flips rather than letterboxing.
|
|
16
|
+
*
|
|
17
|
+
* Known cost on a `left-*` / `right-*` placement: the anchor floor is unclamped
|
|
18
|
+
* so it can drive the flip, and a min beats a max, so a trigger wider than the
|
|
19
|
+
* viewport is floored past the viewport cap. Not new, and not reconciled - see
|
|
20
|
+
* `@atlaskit/top-layer`'s `notes/decisions/width-from-anchor-floors.md`.
|
|
21
|
+
*/
|
|
22
|
+
function getPopupAxisSizes(_ref) {
|
|
23
|
+
var shouldFitContainer = _ref.shouldFitContainer,
|
|
24
|
+
shouldFitViewport = _ref.shouldFitViewport;
|
|
25
|
+
var blockSize = shouldFitViewport ? 'max-available' : 'content';
|
|
26
|
+
if (shouldFitContainer) {
|
|
27
|
+
return {
|
|
28
|
+
inlineSize: 'match-anchor',
|
|
29
|
+
blockSize: blockSize
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (shouldFitViewport) {
|
|
33
|
+
return {
|
|
34
|
+
inlineSize: 'max-available',
|
|
35
|
+
blockSize: blockSize
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
inlineSize: 'content',
|
|
40
|
+
blockSize: blockSize
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* popup-top-layer.tsx generated by @compiled/babel-plugin
|
|
1
|
+
/* popup-top-layer.tsx generated by @compiled/babel-plugin v3.0.1 */
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
@@ -7,27 +7,24 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
value: true
|
|
8
8
|
});
|
|
9
9
|
exports.PopupTopLayer = void 0;
|
|
10
|
-
require("./popup-top-layer.compiled.css");
|
|
11
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
12
11
|
var React = _react;
|
|
13
12
|
var _runtime = require("@compiled/react/runtime");
|
|
14
13
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
14
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
15
15
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
16
16
|
var _getAriaForTrigger = require("@atlaskit/top-layer/get-aria-for-trigger");
|
|
17
17
|
var _index = require("@atlaskit/top-layer/placement-map/index");
|
|
18
18
|
var _createCloseEvent = require("@atlaskit/top-layer/popover/create-close-event");
|
|
19
19
|
var _popover = require("@atlaskit/top-layer/popover/popover");
|
|
20
20
|
var _popoverSurface = require("@atlaskit/top-layer/popover-surface");
|
|
21
|
-
var
|
|
21
|
+
var _useAnchoredPopover = require("@atlaskit/top-layer/use-anchored-popover");
|
|
22
22
|
var _usePopoverId = require("@atlaskit/top-layer/use-popover-id");
|
|
23
|
-
var
|
|
23
|
+
var _getPopupAxisSizes = require("./internal/get-popup-axis-sizes");
|
|
24
24
|
var _topLayerBridge = require("./internal/top-layer-bridge");
|
|
25
25
|
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); }
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
default: ""
|
|
29
|
-
};
|
|
30
|
-
|
|
26
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
27
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
31
28
|
// Top-layer positioning is handled by CSS Anchor Positioning, not inline styles.
|
|
32
29
|
var EMPTY_STYLE = {};
|
|
33
30
|
|
|
@@ -74,7 +71,8 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
|
|
|
74
71
|
_rootBoundary = _ref.rootBoundary,
|
|
75
72
|
_shouldUseCaptureOnOutsideClick = _ref.shouldUseCaptureOnOutsideClick,
|
|
76
73
|
_shouldDisableFocusLock = _ref.shouldDisableFocusLock,
|
|
77
|
-
|
|
74
|
+
_ref$shouldFitViewpor = _ref.shouldFitViewport,
|
|
75
|
+
shouldFitViewport = _ref$shouldFitViewpor === void 0 ? false : _ref$shouldFitViewpor,
|
|
78
76
|
_appearance = _ref.appearance;
|
|
79
77
|
var triggerRef = (0, _react.useRef)(null);
|
|
80
78
|
var popoverRef = (0, _react.useRef)(null);
|
|
@@ -149,18 +147,15 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
|
|
|
149
147
|
offset: offsetProp
|
|
150
148
|
});
|
|
151
149
|
}, [placement, offsetProp]);
|
|
152
|
-
(0,
|
|
150
|
+
(0, _useAnchoredPopover.useAnchoredPopover)(_objectSpread({
|
|
153
151
|
anchorRef: triggerRef,
|
|
154
152
|
popoverRef: popoverRef,
|
|
155
153
|
placement: topLayerPlacement,
|
|
156
154
|
isOpen: isOpen
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
anchorRef: triggerRef,
|
|
162
|
-
isOpen: isOpen
|
|
163
|
-
});
|
|
155
|
+
}, (0, _getPopupAxisSizes.getPopupAxisSizes)({
|
|
156
|
+
shouldFitContainer: shouldFitContainer,
|
|
157
|
+
shouldFitViewport: shouldFitViewport
|
|
158
|
+
})));
|
|
164
159
|
|
|
165
160
|
// onClose bridge.
|
|
166
161
|
// Translates top-layer's { reason: TPopoverCloseReason } into legacy
|
|
@@ -241,7 +236,20 @@ var PopupTopLayer = exports.PopupTopLayer = /*#__PURE__*/(0, _react.memo)(functi
|
|
|
241
236
|
"data-testid": testId,
|
|
242
237
|
tabIndex: autoFocus ? -1 : undefined,
|
|
243
238
|
xcss: xcss
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
// Forwarded because several in-tree containers key their own
|
|
240
|
+
// `overflow: auto` branch off it.
|
|
241
|
+
,
|
|
242
|
+
shouldFitViewport: shouldFitViewport
|
|
243
|
+
}, content(contentProps)) :
|
|
244
|
+
/*#__PURE__*/
|
|
245
|
+
// `PopoverSurface` owns `overflow: auto` and the `box-shadow` on the
|
|
246
|
+
// same element, so it is the scroll container. The wrapper exists only
|
|
247
|
+
// to carry the consumer's `xcss`: `PopoverSurface` exposes no
|
|
248
|
+
// `className` or `xcss` by design, so `xcss` lands on a CHILD of the
|
|
249
|
+
// surface. `width` works there; surface-level declarations apply one
|
|
250
|
+
// level in. Same shape as `compositional/popup-content-top-layer.tsx`.
|
|
251
|
+
React.createElement(_popoverSurface.PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
|
|
252
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
253
|
+
className: xcss
|
|
246
254
|
}, content(contentProps)))));
|
|
247
255
|
});
|
package/dist/cjs/popup.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.usePopupAppearance = usePopupAppearance;
|
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
9
|
var _react = require("react");
|
|
10
|
+
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
10
11
|
var _compiled = require("@atlaskit/primitives/compiled");
|
|
11
12
|
/**
|
|
12
13
|
* **usePopupAppearance()**
|
|
@@ -19,14 +20,21 @@ var _compiled = require("@atlaskit/primitives/compiled");
|
|
|
19
20
|
function usePopupAppearance(_ref) {
|
|
20
21
|
var _appearance = _ref.appearance,
|
|
21
22
|
_shouldRenderToParent = _ref.shouldRenderToParent;
|
|
22
|
-
var
|
|
23
|
+
var canBecomeModal = _appearance === 'UNSAFE_modal-below-sm';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This gate removes unnecessary state changes across the below-sm boundary
|
|
27
|
+
* The state change is only needed if the popup can become a modal - and this is maintained
|
|
28
|
+
*/
|
|
29
|
+
var shouldUseMediaQuery = !(0, _fg.fg)('platform_dst_popup_media_query_perf_fix') || canBecomeModal;
|
|
30
|
+
var mq = (0, _compiled.UNSAFE_useMediaQuery)('below.sm', shouldUseMediaQuery ? function (e) {
|
|
23
31
|
setIsSmallViewport(!!e.matches);
|
|
24
|
-
});
|
|
32
|
+
} : undefined);
|
|
25
33
|
var _useState = (0, _react.useState)(!!(mq !== null && mq !== void 0 && mq.matches)),
|
|
26
34
|
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
27
35
|
isSmallViewport = _useState2[0],
|
|
28
36
|
setIsSmallViewport = _useState2[1];
|
|
29
|
-
var appearance =
|
|
37
|
+
var appearance = canBecomeModal && isSmallViewport ? 'UNSAFE_modal-below-sm' : 'default';
|
|
30
38
|
var shouldRenderToParent = _shouldRenderToParent && appearance === 'default';
|
|
31
39
|
return {
|
|
32
40
|
appearance: appearance,
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
/* popup-content-top-layer.tsx generated by @compiled/babel-plugin
|
|
1
|
+
/* popup-content-top-layer.tsx generated by @compiled/babel-plugin v3.0.1 */
|
|
2
2
|
import _extends from "@babel/runtime/helpers/extends";
|
|
3
|
-
import "./popup-content-top-layer.compiled.css";
|
|
4
3
|
import * as React from 'react';
|
|
4
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
5
5
|
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
|
|
6
|
-
import { ax, ix } from '@compiled/react/runtime';
|
|
7
6
|
import noop from '@atlaskit/ds-lib/noop';
|
|
8
7
|
import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map/index';
|
|
9
8
|
import { createPopoverCloseEvent } from '@atlaskit/top-layer/popover/create-close-event';
|
|
10
9
|
import { Popover } from '@atlaskit/top-layer/popover/popover';
|
|
11
10
|
import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { useAnchoredPopover } from '@atlaskit/top-layer/use-anchored-popover';
|
|
12
|
+
import { getPopupAxisSizes } from '../internal/get-popup-axis-sizes';
|
|
14
13
|
import { useRoleProps } from '../internal/top-layer-bridge';
|
|
15
14
|
import { TriggerRefObjectContext } from './trigger-ref-object-context';
|
|
16
|
-
const overflowAutoStyles = null;
|
|
17
15
|
|
|
18
16
|
// Top-layer positioning is handled by CSS Anchor Positioning, not inline styles.
|
|
19
17
|
const EMPTY_STYLE = {};
|
|
@@ -36,8 +34,9 @@ export function PopupContentTopLayer({
|
|
|
36
34
|
fallbackPlacements: _fallbackPlacements,
|
|
37
35
|
popupComponent: PopupContainer,
|
|
38
36
|
autoFocus = true,
|
|
39
|
-
shouldFitContainer,
|
|
40
|
-
|
|
37
|
+
shouldFitContainer = false,
|
|
38
|
+
// Keep the explicit `false`. See the note in `popup-top-layer.tsx`.
|
|
39
|
+
shouldFitViewport = false,
|
|
41
40
|
role,
|
|
42
41
|
label,
|
|
43
42
|
titleId,
|
|
@@ -176,17 +175,15 @@ export function PopupContentTopLayer({
|
|
|
176
175
|
|
|
177
176
|
// `isOpen` is included so the anchor positioning effect re-runs when
|
|
178
177
|
// the Popover host element is unmounted/remounted across open cycles.
|
|
179
|
-
|
|
178
|
+
useAnchoredPopover({
|
|
180
179
|
anchorRef,
|
|
181
180
|
popoverRef,
|
|
182
181
|
placement: topLayerPlacement,
|
|
183
|
-
isOpen
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
anchorRef,
|
|
189
|
-
isOpen
|
|
182
|
+
isOpen,
|
|
183
|
+
...getPopupAxisSizes({
|
|
184
|
+
shouldFitContainer,
|
|
185
|
+
shouldFitViewport
|
|
186
|
+
})
|
|
190
187
|
});
|
|
191
188
|
|
|
192
189
|
// Narrow to ForwardRefExoticComponent so JSX accepts the ref prop.
|
|
@@ -207,8 +204,20 @@ export function PopupContentTopLayer({
|
|
|
207
204
|
"data-testid": testId,
|
|
208
205
|
tabIndex: autoFocus ? -1 : undefined,
|
|
209
206
|
xcss: xcss
|
|
210
|
-
|
|
207
|
+
// Forwarded because several in-tree containers key their own
|
|
208
|
+
// `overflow: auto` branch off it.
|
|
209
|
+
,
|
|
210
|
+
shouldFitViewport: shouldFitViewport
|
|
211
|
+
}, children(contentProps)) :
|
|
212
|
+
/*#__PURE__*/
|
|
213
|
+
// `PopoverSurface` owns `overflow: auto` and the `box-shadow` on the
|
|
214
|
+
// same element, so it is the scroll container. The wrapper exists only
|
|
215
|
+
// to carry the consumer's `xcss`: `PopoverSurface` exposes no
|
|
216
|
+
// `className` or `xcss` by design, so `xcss` lands on a CHILD of the
|
|
217
|
+
// surface. `width` works there; surface-level declarations apply one
|
|
218
|
+
// level in. Same shape as `../popup-top-layer.tsx`.
|
|
219
|
+
React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", {
|
|
211
220
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
|
|
212
|
-
className:
|
|
221
|
+
className: xcss
|
|
213
222
|
}, children(contentProps))));
|
|
214
223
|
}
|
|
@@ -39,7 +39,7 @@ export const Popup = ({
|
|
|
39
39
|
// When the top-layer flag is on, we maintain a stable RefObject for the
|
|
40
40
|
// trigger element, provided through TriggerRefObjectContext. Unlike the
|
|
41
41
|
// state-based triggerRef (which causes a re-render when the trigger mounts),
|
|
42
|
-
// this ref has a stable identity so
|
|
42
|
+
// this ref has a stable identity so useAnchoredPopover's useLayoutEffect dep
|
|
43
43
|
// array never changes — the effect runs once on mount when the trigger element
|
|
44
44
|
// is already populated. PopupTrigger sets this ref directly instead of calling
|
|
45
45
|
// the state setter, avoiding an unnecessary re-render.
|