@atlaskit/datetime-picker 18.4.0 → 18.4.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,46 @@
1
1
  # @atlaskit/datetime-picker
2
2
 
3
+ ## 18.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6d3790c08ff19`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6d3790c08ff19) -
8
+ Experimental React 19 peer dependency support. This patch widens the peer range; CI coverage is
9
+ partial.
10
+ - Updated dependencies
11
+
12
+ ## 18.4.1
13
+
14
+ ### Patch Changes
15
+
16
+ - [`a7e63cfd84876`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a7e63cfd84876) -
17
+ [ux] When `platform-dst-top-layer` is enabled, Select menus now use the native Popover API to
18
+ handle Escape and light-dismiss before synchronizing `menuIsOpen` through `onMenuClose`. This
19
+ ensures Escape dismisses a nested Select menu without also dismissing its containing popup.
20
+
21
+ Existing public props keep their behavior. The top-layer `Popover` implementation gains an
22
+ internal `source` prop for imperative native popover invokers. The public runtime differences
23
+ behind the feature gate are:
24
+ - Select no longer calls `preventDefault()` or `stopPropagation()` for Escape on the top-layer
25
+ path, whether its menu is open or closed. Successive Escape presses can therefore dismiss the
26
+ Select menu and then its containing native popover.
27
+ - `onMenuClose` is called from the native popover close lifecycle rather than synchronously during
28
+ Select's Escape `keydown` handler.
29
+ - `shouldPreventEscapePropagation` no longer stops Escape propagation while a top-layer menu is
30
+ open; its legacy-path behavior is unchanged.
31
+ - Top-layer Escape invokes `onInputChange` with the `menu-close` action once, after native
32
+ dismissal, instead of the legacy path's two calls.
33
+ - Controlled Selects must continue updating `menuIsOpen` to `false` in response to `onMenuClose`.
34
+ - `PopupSelect` delegates Escape dismissal and focus restoration to its native popover. A consumer
35
+ `onKeyDown` handler can therefore prevent the native dismissal with `preventDefault()`;
36
+ previously, `PopupSelect` closed before forwarding the event.
37
+ - DatePicker's top-layer calendar now uses the Select menu portal's native popover instead of
38
+ creating a nested manual popover.
39
+ - The internal Popover forwarding API accepts a native `source` element so imperative Select
40
+ popovers retain their trigger relationship for light-dismiss behavior.
41
+
42
+ - Updated dependencies
43
+
3
44
  ## 18.4.0
4
45
 
5
46
  ### Minor Changes
@@ -3,19 +3,15 @@ import { expect, test } from '@af/integration-testing';
3
3
  /**
4
4
  * Date picker: focus contract on the top-layer code path.
5
5
  *
6
- * `DatePicker` opens a `role="dialog"` calendar popover anchored to a
7
- * react-select combobox `<input>`. Because the popover uses the dialog role
8
- * (not `menu` / `listbox`), the combobox carve-out documented in
9
- * `top-layer/notes/architecture/focus.md` does NOT apply: dialogs always
10
- * move focus into the popover (to the first focusable, here the
11
- * previous-year navigation button).
6
+ * `DatePicker` renders its calendar inside the top-layer react-select menu.
7
+ * Opening from the input keeps DOM focus on the combobox; the calendar remains
8
+ * reachable in normal Tab order.
12
9
  *
13
10
  * The focus contract is therefore:
14
11
  *
15
- * 1. Open: focus moves to the first focusable inside the calendar dialog
16
- * (the previous-year navigation button).
12
+ * 1. Open from the input: focus remains on the combobox.
17
13
  * 2. Escape: the popover closes and focus is restored to the combobox input.
18
- * 3. Tab inside the open dialog: focus cycles through dialog focusables.
14
+ * 3. Tab: focus moves from the input into the calendar without closing it.
19
15
  *
20
16
  * See: `platform/packages/design-system/top-layer/notes/architecture/focus.md`.
21
17
  */
@@ -23,9 +19,7 @@ import { expect, test } from '@af/integration-testing';
23
19
  const featureFlag = 'platform-dst-top-layer';
24
20
 
25
21
  test.describe('Date picker: top-layer focus contract', () => {
26
- test('initial focus: focus moves to the first focusable in the calendar dialog on open', async ({
27
- page,
28
- }) => {
22
+ test('pointer interaction: clicking the input opens the calendar', async ({ page }) => {
29
23
  await page.visitExample<typeof import('../../examples/testing-top-layer-focus.tsx')>(
30
24
  'design-system',
31
25
  'datetime-picker',
@@ -33,13 +27,27 @@ test.describe('Date picker: top-layer focus contract', () => {
33
27
  { featureFlag },
34
28
  );
35
29
 
36
- await page.getByTestId('date-picker--container').click();
30
+ const combobox = page.getByRole('combobox');
31
+ await combobox.click();
37
32
 
38
- const calendar = page.getByRole('dialog', { name: 'calendar' });
39
- await expect(calendar).toBeVisible();
33
+ await expect(page.getByRole('grid')).toBeVisible();
34
+ await expect(combobox).toHaveAttribute('aria-expanded', 'true');
35
+ });
40
36
 
41
- const previousYearButton = page.getByTestId('date-picker--calendar--previous-year');
42
- await expect(previousYearButton).toBeFocused();
37
+ test('initial focus: opening from the input keeps focus on the combobox', async ({ page }) => {
38
+ await page.visitExample<typeof import('../../examples/testing-top-layer-focus.tsx')>(
39
+ 'design-system',
40
+ 'datetime-picker',
41
+ 'testing-top-layer-focus',
42
+ { featureFlag },
43
+ );
44
+
45
+ const combobox = page.getByRole('combobox');
46
+ await combobox.focus();
47
+
48
+ const calendar = page.getByRole('grid');
49
+ await expect(calendar).toBeVisible();
50
+ await expect(combobox).toBeFocused();
43
51
  });
44
52
 
45
53
  test('focus restoration: Escape closes the popover and focus returns to the combobox input', async ({
@@ -53,9 +61,9 @@ test.describe('Date picker: top-layer focus contract', () => {
53
61
  );
54
62
 
55
63
  const combobox = page.getByRole('combobox');
56
- await page.getByTestId('date-picker--container').click();
64
+ await combobox.focus();
57
65
 
58
- const calendar = page.getByRole('dialog', { name: 'calendar' });
66
+ const calendar = page.getByRole('grid');
59
67
  await expect(calendar).toBeVisible();
60
68
 
61
69
  await page.keyboard.press('Escape');
@@ -63,9 +71,7 @@ test.describe('Date picker: top-layer focus contract', () => {
63
71
  await expect(combobox).toBeFocused();
64
72
  });
65
73
 
66
- test('focus movement: Tab moves focus and keeps it inside the calendar dialog', async ({
67
- page,
68
- }) => {
74
+ test('focus movement: Tab moves focus from the input into the calendar', async ({ page }) => {
69
75
  await page.visitExample<typeof import('../../examples/testing-top-layer-focus.tsx')>(
70
76
  'design-system',
71
77
  'datetime-picker',
@@ -73,25 +79,16 @@ test.describe('Date picker: top-layer focus contract', () => {
73
79
  { featureFlag },
74
80
  );
75
81
 
76
- await page.getByTestId('date-picker--container').click();
82
+ const combobox = page.getByRole('combobox');
83
+ await combobox.focus();
77
84
 
78
- const calendar = page.getByRole('dialog', { name: 'calendar' });
85
+ const calendar = page.getByRole('grid');
79
86
  await expect(calendar).toBeVisible();
87
+ await expect(combobox).toBeFocused();
80
88
 
81
89
  const previousYearButton = page.getByTestId('date-picker--calendar--previous-year');
82
- await expect(previousYearButton).toBeFocused();
83
-
84
- // `useFocusWrap` keeps Tab inside the dialog for role="dialog" popovers.
85
90
  await page.keyboard.press('Tab');
86
- await expect(previousYearButton).not.toBeFocused();
87
-
88
- // Focus must stay within the calendar dialog: for a role="dialog" popover
89
- // `useFocusWrap` intercepts Tab so focus never escapes to page content
90
- // behind the popover.
91
- const focusWithinCalendar = await calendar.evaluate(
92
- (calendarElement) =>
93
- document.activeElement !== null && calendarElement.contains(document.activeElement),
94
- );
95
- expect(focusWithinCalendar).toBe(true);
91
+ await expect(previousYearButton).toBeFocused();
92
+ await expect(calendar).toBeVisible();
96
93
  });
97
94
  });
@@ -17,7 +17,7 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
17
17
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
18
18
  var _dateFns = require("date-fns");
19
19
  var _usePlatformLeafEventHandler = require("@atlaskit/analytics-next/usePlatformLeafEventHandler");
20
- var _new = require("@atlaskit/button/new");
20
+ var _button = _interopRequireDefault(require("@atlaskit/button/icon/button"));
21
21
  var _css = require("@atlaskit/css");
22
22
  var _useId = require("@atlaskit/ds-lib/use-id");
23
23
  var _calendar = _interopRequireDefault(require("@atlaskit/icon/core/calendar"));
@@ -45,7 +45,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
45
45
  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; }
46
46
  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; }
47
47
  var packageName = "@atlaskit/datetime-picker";
48
- var packageVersion = "18.3.1";
48
+ var packageVersion = "18.4.1";
49
49
  var styles = {
50
50
  pickerContainerStyle: "_kqswh2mm",
51
51
  dropdownIndicatorStyles: "_1ul91k8s _1tke1k8s _1e0c1txw _4cvr1h6o _1bah1h6o",
@@ -72,6 +72,9 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
72
72
  var containerRef = (0, _react.useRef)(null);
73
73
  var calendarRef = (0, _react.useRef)(null);
74
74
  var calendarButtonRef = (0, _react.useRef)(null);
75
+ // Track whether focus was caused by an active pointer gesture. Pointer-driven focus must defer
76
+ // opening a top-layer popover until click, while keyboard and programmatic focus can open it now.
77
+ var isPointerInteractionRef = (0, _react.useRef)(false);
75
78
  var _props$appearance = props.appearance,
76
79
  appearance = _props$appearance === void 0 ? 'default' : _props$appearance,
77
80
  _props$autoFocus = props.autoFocus,
@@ -197,19 +200,15 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
197
200
  setLocale(propLocale);
198
201
  }
199
202
  (0, _react.useEffect)(function () {
200
- // We don't want the focus to move if this is a click event
203
+ // Only move focus when the dedicated calendar button opened the menu.
201
204
  if (!isKeyDown) {
202
205
  return;
203
206
  }
204
207
  if (isOpen && wasOpenedFromCalendarButton) {
208
+ var _calendarRef$current;
205
209
  setIsKeyDown(false);
206
- // When using top-layer, Popup.Content with role='dialog' automatically
207
- // focuses the first focusable element — no manual focus needed.
208
- if (!(0, _fg.fg)('platform-dst-top-layer')) {
209
- var _calendarRef$current;
210
- // Focus on the first button within the calendar
211
- calendarRef === null || calendarRef === void 0 || (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 || (_calendarRef$current = _calendarRef$current.querySelector('button')) === null || _calendarRef$current === void 0 || _calendarRef$current.focus();
212
- }
210
+ // Focus on the first button within the calendar
211
+ calendarRef === null || calendarRef === void 0 || (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 || (_calendarRef$current = _calendarRef$current.querySelector('button')) === null || _calendarRef$current === void 0 || _calendarRef$current.focus();
213
212
  }
214
213
  }, [isKeyDown, calendarRef, isOpen, wasOpenedFromCalendarButton]);
215
214
  var getValue = function getValue() {
@@ -256,6 +255,7 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
256
255
  var onContainerBlur = function onContainerBlur(event) {
257
256
  var _containerRef$current2;
258
257
  var newlyFocusedElement = event.relatedTarget;
258
+ isPointerInteractionRef.current = false;
259
259
  if (!(containerRef !== null && containerRef !== void 0 && (_containerRef$current2 = containerRef.current) !== null && _containerRef$current2 !== void 0 && _containerRef$current2.contains(newlyFocusedElement))) {
260
260
  setIsOpen(false);
261
261
  setShouldSetFocusOnCurrentDay(false);
@@ -286,8 +286,12 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
286
286
  // Don't open menu if focussing after the user has clicked clear
287
287
  setClearingFromIcon(false);
288
288
  } else {
289
- // Don't open when focused into via keyboard if the calendar button is present
290
- setIsOpen(!shouldShowCalendarButton);
289
+ // Opening an auto popover during pointer focus allows the matching pointerup
290
+ // to immediately light-dismiss it. Let the subsequent click open it instead.
291
+ if (!(0, _fg.fg)('platform-dst-top-layer') || !isPointerInteractionRef.current) {
292
+ // Don't open when focused into via keyboard if the calendar button is present
293
+ setIsOpen(!shouldShowCalendarButton);
294
+ }
291
295
  setCalendarValue(value);
292
296
  setIsFocused(true);
293
297
  setWasOpenedFromCalendarButton(false);
@@ -323,12 +327,7 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
323
327
  }
324
328
  switch (keyPressed) {
325
329
  case 'escape':
326
- // Restore focus on close. Both code paths handle this here:
327
- // the legacy path because it has no built-in restoration, and
328
- // the top-layer path because the menu uses `mode="manual"` to
329
- // avoid the auto-popover light-dismiss closing the menu on the
330
- // same click that opens it (see internal/menu-top-layer.tsx).
331
- // `manual` mode disables the browser's native focus return.
330
+ // Restore focus because it may have moved from the input into the calendar.
332
331
  if (wasOpenedFromCalendarButton) {
333
332
  var _calendarButtonRef$cu2;
334
333
  (_calendarButtonRef$cu2 = calendarButtonRef.current) === null || _calendarButtonRef$cu2 === void 0 || _calendarButtonRef$cu2.focus();
@@ -406,6 +405,9 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
406
405
 
407
406
  // This event handler is triggered from both keydown and click. It's weird.
408
407
  var onCalendarButtonClick = function onCalendarButtonClick(e) {
408
+ if (!getIsOpen() && (0, _fg.fg)('platform-dst-top-layer')) {
409
+ setIsKeyDown(true);
410
+ }
409
411
  setIsOpen(function (isOpen) {
410
412
  if (isOpen) {
411
413
  var _props$selectProps2, _props$selectProps2$o;
@@ -535,11 +537,28 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
535
537
  var openCalendarLabelId = "open-calendar-label--".concat((0, _useId.useId)());
536
538
  return /*#__PURE__*/React.createElement("div", (0, _extends2.default)({}, innerProps, {
537
539
  "data-testid": testId && "".concat(testId, "--container"),
538
- onBlur: onContainerBlur,
540
+ onBlur: onContainerBlur
541
+ // Reset before descendant click handlers run: click happens after native light-dismiss, so
542
+ // any focus they move is safe to treat as non-pointer focus. Capture also guarantees cleanup
543
+ // when a descendant stops the bubbling click.
544
+ ,
545
+ onClickCapture: function onClickCapture() {
546
+ isPointerInteractionRef.current = false;
547
+ },
539
548
  onFocus: onContainerFocus,
540
549
  onClick: onInputClick,
541
550
  onInput: onTextInput,
542
- onKeyDown: onInputKeyDown,
551
+ onKeyDown: onInputKeyDown
552
+ // A cancelled pointer produces no click, so clear the tracking state here instead.
553
+ ,
554
+ onPointerCancelCapture: function onPointerCancelCapture() {
555
+ isPointerInteractionRef.current = false;
556
+ }
557
+ // Capture pointerdown before it moves focus into a descendant Select control.
558
+ ,
559
+ onPointerDownCapture: function onPointerDownCapture() {
560
+ isPointerInteractionRef.current = true;
561
+ },
543
562
  ref: getContainerRef
544
563
  // Since the onclick, onfocus are passed down, adding role="presentation" prevents typecheck errors.
545
564
  ,
@@ -611,7 +630,7 @@ var DatePicker = /*#__PURE__*/(0, _react.forwardRef)(function (props, _forwarded
611
630
  menuInnerWrapper: calendarProps.menuInnerWrapper
612
631
  })), shouldShowCalendarButton && !isDisabled ? /*#__PURE__*/React.createElement(_compiled.Box, {
613
632
  xcss: (0, _css.cx)(styles.iconContainerStyles, getterValue && !hideIcon ? styles.iconSpacingWithClearButtonStyles : styles.iconSpacingWithoutClearButtonStyles)
614
- }, /*#__PURE__*/React.createElement(_new.IconButton, {
633
+ }, /*#__PURE__*/React.createElement(_button.default, {
615
634
  appearance: "subtle",
616
635
  label: !inputLabelId ? fullopenCalendarLabel : openCalendarLabel,
617
636
  "aria-labelledby": inputLabelId ? "".concat(inputLabelId, " ").concat(openCalendarLabelId) : undefined,
@@ -16,7 +16,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
16
16
  var _react = _interopRequireWildcard(require("react"));
17
17
  var _dateFns = require("date-fns");
18
18
  var _usePlatformLeafEventHandler = require("@atlaskit/analytics-next/usePlatformLeafEventHandler");
19
- var _new = require("@atlaskit/button/new");
19
+ var _button = _interopRequireDefault(require("@atlaskit/button/icon/button"));
20
20
  var _crossCircle = _interopRequireDefault(require("@atlaskit/icon/core/cross-circle"));
21
21
  var _compiled = require("@atlaskit/primitives/compiled");
22
22
  var _styles = require("@atlaskit/react-select/styles");
@@ -33,7 +33,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
33
33
  // for typing `process`
34
34
  // oxlint-disable-next-line @atlassian/no-restricted-imports
35
35
  var packageName = "@atlaskit/datetime-picker";
36
- var packageVersion = "18.3.1";
36
+ var packageVersion = "18.4.1";
37
37
  var analyticsAttributes = {
38
38
  componentName: 'dateTimePicker',
39
39
  packageName: packageName,
@@ -492,7 +492,7 @@ var DateTimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) {
492
492
  value: dtState.timeValue
493
493
  })), isClearable && !isDisabled ? /*#__PURE__*/_react.default.createElement(_compiled.Inline, {
494
494
  xcss: compiledStyles.iconContainerStyles
495
- }, /*#__PURE__*/_react.default.createElement(_new.IconButton, {
495
+ }, /*#__PURE__*/_react.default.createElement(_button.default, {
496
496
  appearance: "subtle",
497
497
  label: clearControlLabel,
498
498
  icon: function icon(iconProps) {
@@ -32,7 +32,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
32
32
  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; }
33
33
  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; } // oxlint-disable-next-line @atlassian/no-restricted-imports
34
34
  var packageName = "@atlaskit/datetime-picker";
35
- var packageVersion = "18.3.1";
35
+ var packageVersion = "18.4.1";
36
36
  var defaultTimeFormat = 'h:mma';
37
37
  var menuStyles = {
38
38
  /* Need to remove default absolute positioning as that causes issues with position fixed */
@@ -13,8 +13,8 @@ var _runtime = require("@compiled/react/runtime");
13
13
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
14
14
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
15
15
  var _components = require("@atlaskit/react-select/components");
16
- var _placementMap = require("@atlaskit/top-layer/placement-map");
17
- var _popover = require("@atlaskit/top-layer/popover");
16
+ var _index = require("@atlaskit/top-layer/placement-map/index");
17
+ var _popover = require("@atlaskit/top-layer/popover/popover");
18
18
  var _popoverSurface = require("@atlaskit/top-layer/popover-surface");
19
19
  var _useAnchorPosition = require("@atlaskit/top-layer/use-anchor-position");
20
20
  var _excluded = ["className", "clearValue", "cx", "getStyles", "getValue", "hasValue", "innerProps", "innerRef", "isLoading", "isMulti", "isRtl", "maxMenuHeight", "menuPlacement", "menuPosition", "menuShouldScrollIntoView", "minMenuHeight", "options", "placement", "selectOption", "selectProps", "setValue", "children"];
@@ -23,7 +23,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
23
23
  * Bottom-start placement: time options menu appears below and aligned to the
24
24
  * start edge of the trigger (the select input).
25
25
  */
26
- var popupPlacement = (0, _placementMap.fromLegacyPlacement)({
26
+ var popupPlacement = (0, _index.fromLegacyPlacement)({
27
27
  legacy: 'bottom-start'
28
28
  });
29
29
 
@@ -0,0 +1,5 @@
1
+ ._2rko1mok{border-radius:var(--ds-radius-large,8px)}._16qs130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
2
+ ._1bsb1ris{width:max-content}
3
+ ._1pfhu2gc{margin-block-start:var(--ds-space-100,8px)}
4
+ ._6rthu2gc{margin-block-end:var(--ds-space-100,8px)}
5
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
@@ -7,25 +7,14 @@ Object.defineProperty(exports, "__esModule", {
7
7
  value: true
8
8
  });
9
9
  exports.MenuTopLayer = void 0;
10
+ require("./menu-top-layer.compiled.css");
10
11
  var _react = _interopRequireWildcard(require("react"));
11
12
  var React = _react;
12
13
  var _runtime = require("@compiled/react/runtime");
13
14
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
14
15
  var _dateFns = require("date-fns");
15
16
  var _calendar = _interopRequireDefault(require("@atlaskit/calendar/calendar"));
16
- var _placementMap = require("@atlaskit/top-layer/placement-map");
17
- var _popover = require("@atlaskit/top-layer/popover");
18
- var _popoverSurface = require("@atlaskit/top-layer/popover-surface");
19
- var _useAnchorPosition = require("@atlaskit/top-layer/use-anchor-position");
20
17
  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); }
21
- /**
22
- * Bottom-start placement: calendar appears below and aligned to the
23
- * start edge of the trigger (the select input).
24
- */
25
- var popupPlacement = (0, _placementMap.fromLegacyPlacement)({
26
- legacy: 'bottom-start'
27
- });
28
-
29
18
  /**
30
19
  * @param isos A series of ISO dates.
31
20
  * @returns The last valid date within the array of ISO strings.
@@ -40,19 +29,19 @@ function getValidDate(isos) {
40
29
  } : acc;
41
30
  }, {});
42
31
  }
32
+ var styles = {
33
+ root: "_2rko1mok _1bsb1ris _bfhk1bhr _16qs130s _1pfhu2gc _6rthu2gc"
34
+ };
43
35
 
44
36
  /**
45
37
  * Top-layer version of the date picker menu.
46
38
  *
47
- * Uses `Popover` + `useAnchorPosition` so the calendar renders in the
48
- * browser's top layer via the native Popover API and is positioned via
49
- * CSS Anchor Positioning. This avoids overflow clipping, z-index wars,
50
- * and portal-based layering.
39
+ * The surrounding react-select menu portal owns the native popover and
40
+ * positioning. This component only supplies the calendar dialog content.
51
41
  *
52
42
  * Gated behind the `platform-dst-top-layer` feature flag.
53
43
  */
54
44
  var MenuTopLayer = exports.MenuTopLayer = function MenuTopLayer(_ref) {
55
- var _selectProps$calendar, _selectProps$calendar2;
56
45
  var selectProps = _ref.selectProps,
57
46
  innerProps = _ref.innerProps;
58
47
  var calendarValue = selectProps.calendarValue,
@@ -62,20 +51,6 @@ var MenuTopLayer = exports.MenuTopLayer = function MenuTopLayer(_ref) {
62
51
  day = _getValidDate.day,
63
52
  month = _getValidDate.month,
64
53
  year = _getValidDate.year;
65
-
66
- // The select's container element is the anchor for the popup.
67
- var triggerRef = (0, _react.useRef)((_selectProps$calendar = selectProps.calendarContainerRef) !== null && _selectProps$calendar !== void 0 ? _selectProps$calendar : null);
68
- triggerRef.current = (_selectProps$calendar2 = selectProps.calendarContainerRef) !== null && _selectProps$calendar2 !== void 0 ? _selectProps$calendar2 : null;
69
- var popoverRef = (0, _react.useRef)(null);
70
- (0, _useAnchorPosition.useAnchorPosition)({
71
- anchorRef: triggerRef,
72
- popoverRef: popoverRef,
73
- placement: popupPlacement,
74
- // The Popover is rendered with `isOpen` always true while this
75
- // component is mounted (react-select conditionally mounts/unmounts
76
- // the menu), so positioning is always active.
77
- isOpen: true
78
- });
79
54
  var onMenuMouseDown = function onMenuMouseDown(event) {
80
55
  if (event.button !== 0) {
81
56
  return;
@@ -84,25 +59,10 @@ var MenuTopLayer = exports.MenuTopLayer = function MenuTopLayer(_ref) {
84
59
  event.preventDefault();
85
60
  };
86
61
  var Wrapper = typeof MenuInnerWrapper === 'function' ? MenuInnerWrapper : _react.Fragment;
87
- return /*#__PURE__*/React.createElement(_popover.Popover, {
88
- ref: popoverRef,
89
- role: "dialog",
90
- label: "calendar",
91
- isOpen: true
92
- // `mode="manual"` opts out of native popover light-dismiss.
93
- // react-select / DateTimePicker already own outside-click and
94
- // Esc handling. Without this, the same click that opens the
95
- // menu (which targets the select input outside the popover)
96
- // also bubbles to the browser's auto-dismiss handler and
97
- // immediately closes the popover.
98
- ,
99
- mode: "manual",
100
- placement: popupPlacement,
101
- shouldAnimate: true,
102
- testId: selectProps.testId && "".concat(selectProps.testId, "--popup")
103
- }, /*#__PURE__*/React.createElement(_popoverSurface.PopoverSurface, null, /*#__PURE__*/React.createElement("div", (0, _extends2.default)({}, innerProps, {
62
+ return /*#__PURE__*/React.createElement("div", (0, _extends2.default)({}, innerProps, {
63
+ onMouseDown: onMenuMouseDown,
104
64
  role: "presentation",
105
- onMouseDown: onMenuMouseDown
65
+ className: (0, _runtime.ax)([styles.root])
106
66
  }), /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(_calendar.default, {
107
67
  day: day,
108
68
  month: month,
@@ -121,5 +81,5 @@ var MenuTopLayer = exports.MenuTopLayer = function MenuTopLayer(_ref) {
121
81
  locale: selectProps.calendarLocale,
122
82
  testId: selectProps.testId && "".concat(selectProps.testId, "--calendar"),
123
83
  weekStartDay: selectProps.calendarWeekStartDay
124
- })))));
84
+ })));
125
85
  };
@@ -10,7 +10,7 @@ import { forwardRef, useCallback, useEffect, useReducer, useRef, useState } from
10
10
  // oxlint-disable-next-line @atlassian/no-restricted-imports
11
11
  import { isValid, parseISO } from 'date-fns';
12
12
  import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler';
13
- import { IconButton } from '@atlaskit/button/new';
13
+ import IconButton from '@atlaskit/button/icon/button';
14
14
  import { cx } from '@atlaskit/css';
15
15
  import { useId } from '@atlaskit/ds-lib/use-id';
16
16
  import CalendarIcon from '@atlaskit/icon/core/calendar';
@@ -32,7 +32,7 @@ import { MenuTopLayer } from '../internal/menu-top-layer';
32
32
  import { parseDate } from '../internal/parse-date';
33
33
  import { makeSingleValue } from '../internal/single-value';
34
34
  const packageName = "@atlaskit/datetime-picker";
35
- const packageVersion = "18.3.1";
35
+ const packageVersion = "18.4.1";
36
36
  const styles = {
37
37
  pickerContainerStyle: "_kqswh2mm",
38
38
  dropdownIndicatorStyles: "_1ul91k8s _1tke1k8s _1e0c1txw _4cvr1h6o _1bah1h6o",
@@ -59,6 +59,9 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
59
59
  const containerRef = useRef(null);
60
60
  const calendarRef = useRef(null);
61
61
  const calendarButtonRef = useRef(null);
62
+ // Track whether focus was caused by an active pointer gesture. Pointer-driven focus must defer
63
+ // opening a top-layer popover until click, while keyboard and programmatic focus can open it now.
64
+ const isPointerInteractionRef = useRef(false);
62
65
  const {
63
66
  appearance = 'default',
64
67
  autoFocus = false,
@@ -126,19 +129,15 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
126
129
  setLocale(propLocale);
127
130
  }
128
131
  useEffect(() => {
129
- // We don't want the focus to move if this is a click event
132
+ // Only move focus when the dedicated calendar button opened the menu.
130
133
  if (!isKeyDown) {
131
134
  return;
132
135
  }
133
136
  if (isOpen && wasOpenedFromCalendarButton) {
137
+ var _calendarRef$current, _calendarRef$current$;
134
138
  setIsKeyDown(false);
135
- // When using top-layer, Popup.Content with role='dialog' automatically
136
- // focuses the first focusable element — no manual focus needed.
137
- if (!fg('platform-dst-top-layer')) {
138
- var _calendarRef$current, _calendarRef$current$;
139
- // Focus on the first button within the calendar
140
- calendarRef === null || calendarRef === void 0 ? void 0 : (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 ? void 0 : (_calendarRef$current$ = _calendarRef$current.querySelector('button')) === null || _calendarRef$current$ === void 0 ? void 0 : _calendarRef$current$.focus();
141
- }
139
+ // Focus on the first button within the calendar
140
+ calendarRef === null || calendarRef === void 0 ? void 0 : (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 ? void 0 : (_calendarRef$current$ = _calendarRef$current.querySelector('button')) === null || _calendarRef$current$ === void 0 ? void 0 : _calendarRef$current$.focus();
142
141
  }
143
142
  }, [isKeyDown, calendarRef, isOpen, wasOpenedFromCalendarButton]);
144
143
  const getValue = () => propValue !== null && propValue !== void 0 ? propValue : value;
@@ -183,6 +182,7 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
183
182
  const onContainerBlur = event => {
184
183
  var _containerRef$current2;
185
184
  const newlyFocusedElement = event.relatedTarget;
185
+ isPointerInteractionRef.current = false;
186
186
  if (!(containerRef !== null && containerRef !== void 0 && (_containerRef$current2 = containerRef.current) !== null && _containerRef$current2 !== void 0 && _containerRef$current2.contains(newlyFocusedElement))) {
187
187
  setIsOpen(false);
188
188
  setShouldSetFocusOnCurrentDay(false);
@@ -213,8 +213,12 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
213
213
  // Don't open menu if focussing after the user has clicked clear
214
214
  setClearingFromIcon(false);
215
215
  } else {
216
- // Don't open when focused into via keyboard if the calendar button is present
217
- setIsOpen(!shouldShowCalendarButton);
216
+ // Opening an auto popover during pointer focus allows the matching pointerup
217
+ // to immediately light-dismiss it. Let the subsequent click open it instead.
218
+ if (!fg('platform-dst-top-layer') || !isPointerInteractionRef.current) {
219
+ // Don't open when focused into via keyboard if the calendar button is present
220
+ setIsOpen(!shouldShowCalendarButton);
221
+ }
218
222
  setCalendarValue(value);
219
223
  setIsFocused(true);
220
224
  setWasOpenedFromCalendarButton(false);
@@ -250,12 +254,7 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
250
254
  }
251
255
  switch (keyPressed) {
252
256
  case 'escape':
253
- // Restore focus on close. Both code paths handle this here:
254
- // the legacy path because it has no built-in restoration, and
255
- // the top-layer path because the menu uses `mode="manual"` to
256
- // avoid the auto-popover light-dismiss closing the menu on the
257
- // same click that opens it (see internal/menu-top-layer.tsx).
258
- // `manual` mode disables the browser's native focus return.
257
+ // Restore focus because it may have moved from the input into the calendar.
259
258
  if (wasOpenedFromCalendarButton) {
260
259
  var _calendarButtonRef$cu2;
261
260
  (_calendarButtonRef$cu2 = calendarButtonRef.current) === null || _calendarButtonRef$cu2 === void 0 ? void 0 : _calendarButtonRef$cu2.focus();
@@ -333,6 +332,9 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
333
332
 
334
333
  // This event handler is triggered from both keydown and click. It's weird.
335
334
  const onCalendarButtonClick = e => {
335
+ if (!getIsOpen() && fg('platform-dst-top-layer')) {
336
+ setIsKeyDown(true);
337
+ }
336
338
  setIsOpen(isOpen => {
337
339
  if (isOpen) {
338
340
  var _props$selectProps, _props$selectProps$on;
@@ -463,11 +465,28 @@ const DatePicker = /*#__PURE__*/forwardRef((props, _forwardedRef) => {
463
465
  const openCalendarLabelId = `open-calendar-label--${useId()}`;
464
466
  return /*#__PURE__*/React.createElement("div", _extends({}, innerProps, {
465
467
  "data-testid": testId && `${testId}--container`,
466
- onBlur: onContainerBlur,
468
+ onBlur: onContainerBlur
469
+ // Reset before descendant click handlers run: click happens after native light-dismiss, so
470
+ // any focus they move is safe to treat as non-pointer focus. Capture also guarantees cleanup
471
+ // when a descendant stops the bubbling click.
472
+ ,
473
+ onClickCapture: () => {
474
+ isPointerInteractionRef.current = false;
475
+ },
467
476
  onFocus: onContainerFocus,
468
477
  onClick: onInputClick,
469
478
  onInput: onTextInput,
470
- onKeyDown: onInputKeyDown,
479
+ onKeyDown: onInputKeyDown
480
+ // A cancelled pointer produces no click, so clear the tracking state here instead.
481
+ ,
482
+ onPointerCancelCapture: () => {
483
+ isPointerInteractionRef.current = false;
484
+ }
485
+ // Capture pointerdown before it moves focus into a descendant Select control.
486
+ ,
487
+ onPointerDownCapture: () => {
488
+ isPointerInteractionRef.current = true;
489
+ },
471
490
  ref: getContainerRef
472
491
  // Since the onclick, onfocus are passed down, adding role="presentation" prevents typecheck errors.
473
492
  ,
@@ -10,7 +10,7 @@ import React, { forwardRef, useCallback, useEffect, useReducer, useRef, useState
10
10
  // oxlint-disable-next-line @atlassian/no-restricted-imports
11
11
  import { format, isValid, parseISO } from 'date-fns';
12
12
  import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler';
13
- import { IconButton } from '@atlaskit/button/new';
13
+ import IconButton from '@atlaskit/button/icon/button';
14
14
  import SelectClearIcon from '@atlaskit/icon/core/cross-circle';
15
15
  import { Box, Inline } from '@atlaskit/primitives/compiled';
16
16
  import { mergeStyles } from '@atlaskit/react-select/styles';
@@ -20,7 +20,7 @@ import { convertTokens } from '../internal/parse-tokens';
20
20
  import DatePicker from './date-picker';
21
21
  import TimePicker from './time-picker';
22
22
  const packageName = "@atlaskit/datetime-picker";
23
- const packageVersion = "18.3.1";
23
+ const packageVersion = "18.4.1";
24
24
  const analyticsAttributes = {
25
25
  componentName: 'dateTimePicker',
26
26
  packageName,
@@ -19,7 +19,7 @@ import { convertTokens } from '../internal/parse-tokens';
19
19
  import { placeholderDatetime } from '../internal/placeholder-date-time';
20
20
  import { makeSingleValue } from '../internal/single-value';
21
21
  const packageName = "@atlaskit/datetime-picker";
22
- const packageVersion = "18.3.1";
22
+ const packageVersion = "18.4.1";
23
23
  const defaultTimeFormat = 'h:mma';
24
24
  const menuStyles = {
25
25
  /* Need to remove default absolute positioning as that causes issues with position fixed */
@@ -4,8 +4,8 @@ import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
5
  import { useRef } from 'react';
6
6
  import { components } from '@atlaskit/react-select/components';
7
- import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
8
- import { Popover } from '@atlaskit/top-layer/popover';
7
+ import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map/index';
8
+ import { Popover } from '@atlaskit/top-layer/popover/popover';
9
9
  import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
10
10
  import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
11
11
 
@@ -0,0 +1,5 @@
1
+ ._2rko1mok{border-radius:var(--ds-radius-large,8px)}._16qs130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
2
+ ._1bsb1ris{width:max-content}
3
+ ._1pfhu2gc{margin-block-start:var(--ds-space-100,8px)}
4
+ ._6rthu2gc{margin-block-end:var(--ds-space-100,8px)}
5
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
@@ -1,23 +1,11 @@
1
1
  /* menu-top-layer.tsx generated by @compiled/babel-plugin v2.0.0 */
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
+ import "./menu-top-layer.compiled.css";
3
4
  import * as React from 'react';
4
5
  import { ax, ix } from "@compiled/react/runtime";
5
- import { Fragment, useRef } from 'react';
6
+ import { Fragment } from 'react';
6
7
  import { isValid, parseISO } from 'date-fns';
7
8
  import Calendar from '@atlaskit/calendar/calendar';
8
- import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
9
- import { Popover } from '@atlaskit/top-layer/popover';
10
- import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
11
- import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
12
-
13
- /**
14
- * Bottom-start placement: calendar appears below and aligned to the
15
- * start edge of the trigger (the select input).
16
- */
17
- const popupPlacement = fromLegacyPlacement({
18
- legacy: 'bottom-start'
19
- });
20
-
21
9
  /**
22
10
  * @param isos A series of ISO dates.
23
11
  * @returns The last valid date within the array of ISO strings.
@@ -32,14 +20,15 @@ function getValidDate(isos) {
32
20
  } : acc;
33
21
  }, {});
34
22
  }
23
+ const styles = {
24
+ root: "_2rko1mok _1bsb1ris _bfhk1bhr _16qs130s _1pfhu2gc _6rthu2gc"
25
+ };
35
26
 
36
27
  /**
37
28
  * Top-layer version of the date picker menu.
38
29
  *
39
- * Uses `Popover` + `useAnchorPosition` so the calendar renders in the
40
- * browser's top layer via the native Popover API and is positioned via
41
- * CSS Anchor Positioning. This avoids overflow clipping, z-index wars,
42
- * and portal-based layering.
30
+ * The surrounding react-select menu portal owns the native popover and
31
+ * positioning. This component only supplies the calendar dialog content.
43
32
  *
44
33
  * Gated behind the `platform-dst-top-layer` feature flag.
45
34
  */
@@ -47,7 +36,6 @@ export const MenuTopLayer = ({
47
36
  selectProps,
48
37
  innerProps
49
38
  }) => {
50
- var _selectProps$calendar, _selectProps$calendar2;
51
39
  const {
52
40
  calendarValue,
53
41
  calendarView,
@@ -58,20 +46,6 @@ export const MenuTopLayer = ({
58
46
  month,
59
47
  year
60
48
  } = getValidDate([calendarValue, calendarView]);
61
-
62
- // The select's container element is the anchor for the popup.
63
- const triggerRef = useRef((_selectProps$calendar = selectProps.calendarContainerRef) !== null && _selectProps$calendar !== void 0 ? _selectProps$calendar : null);
64
- triggerRef.current = (_selectProps$calendar2 = selectProps.calendarContainerRef) !== null && _selectProps$calendar2 !== void 0 ? _selectProps$calendar2 : null;
65
- const popoverRef = useRef(null);
66
- useAnchorPosition({
67
- anchorRef: triggerRef,
68
- popoverRef,
69
- placement: popupPlacement,
70
- // The Popover is rendered with `isOpen` always true while this
71
- // component is mounted (react-select conditionally mounts/unmounts
72
- // the menu), so positioning is always active.
73
- isOpen: true
74
- });
75
49
  const onMenuMouseDown = event => {
76
50
  if (event.button !== 0) {
77
51
  return;
@@ -80,25 +54,10 @@ export const MenuTopLayer = ({
80
54
  event.preventDefault();
81
55
  };
82
56
  const Wrapper = typeof MenuInnerWrapper === 'function' ? MenuInnerWrapper : Fragment;
83
- return /*#__PURE__*/React.createElement(Popover, {
84
- ref: popoverRef,
85
- role: "dialog",
86
- label: "calendar",
87
- isOpen: true
88
- // `mode="manual"` opts out of native popover light-dismiss.
89
- // react-select / DateTimePicker already own outside-click and
90
- // Esc handling. Without this, the same click that opens the
91
- // menu (which targets the select input outside the popover)
92
- // also bubbles to the browser's auto-dismiss handler and
93
- // immediately closes the popover.
94
- ,
95
- mode: "manual",
96
- placement: popupPlacement,
97
- shouldAnimate: true,
98
- testId: selectProps.testId && `${selectProps.testId}--popup`
99
- }, /*#__PURE__*/React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", _extends({}, innerProps, {
57
+ return /*#__PURE__*/React.createElement("div", _extends({}, innerProps, {
58
+ onMouseDown: onMenuMouseDown,
100
59
  role: "presentation",
101
- onMouseDown: onMenuMouseDown
60
+ className: ax([styles.root])
102
61
  }), /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Calendar, {
103
62
  day: day,
104
63
  month: month,
@@ -117,5 +76,5 @@ export const MenuTopLayer = ({
117
76
  locale: selectProps.calendarLocale,
118
77
  testId: selectProps.testId && `${selectProps.testId}--calendar`,
119
78
  weekStartDay: selectProps.calendarWeekStartDay
120
- })))));
79
+ })));
121
80
  };
@@ -16,7 +16,7 @@ import { forwardRef, useCallback, useEffect, useReducer, useRef, useState } from
16
16
  // oxlint-disable-next-line @atlassian/no-restricted-imports
17
17
  import { isValid, parseISO } from 'date-fns';
18
18
  import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler';
19
- import { IconButton } from '@atlaskit/button/new';
19
+ import IconButton from '@atlaskit/button/icon/button';
20
20
  import { cx } from '@atlaskit/css';
21
21
  import { useId } from '@atlaskit/ds-lib/use-id';
22
22
  import CalendarIcon from '@atlaskit/icon/core/calendar';
@@ -38,7 +38,7 @@ import { MenuTopLayer } from '../internal/menu-top-layer';
38
38
  import { parseDate } from '../internal/parse-date';
39
39
  import { makeSingleValue } from '../internal/single-value';
40
40
  var packageName = "@atlaskit/datetime-picker";
41
- var packageVersion = "18.3.1";
41
+ var packageVersion = "18.4.1";
42
42
  var styles = {
43
43
  pickerContainerStyle: "_kqswh2mm",
44
44
  dropdownIndicatorStyles: "_1ul91k8s _1tke1k8s _1e0c1txw _4cvr1h6o _1bah1h6o",
@@ -65,6 +65,9 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
65
65
  var containerRef = useRef(null);
66
66
  var calendarRef = useRef(null);
67
67
  var calendarButtonRef = useRef(null);
68
+ // Track whether focus was caused by an active pointer gesture. Pointer-driven focus must defer
69
+ // opening a top-layer popover until click, while keyboard and programmatic focus can open it now.
70
+ var isPointerInteractionRef = useRef(false);
68
71
  var _props$appearance = props.appearance,
69
72
  appearance = _props$appearance === void 0 ? 'default' : _props$appearance,
70
73
  _props$autoFocus = props.autoFocus,
@@ -190,19 +193,15 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
190
193
  setLocale(propLocale);
191
194
  }
192
195
  useEffect(function () {
193
- // We don't want the focus to move if this is a click event
196
+ // Only move focus when the dedicated calendar button opened the menu.
194
197
  if (!isKeyDown) {
195
198
  return;
196
199
  }
197
200
  if (isOpen && wasOpenedFromCalendarButton) {
201
+ var _calendarRef$current;
198
202
  setIsKeyDown(false);
199
- // When using top-layer, Popup.Content with role='dialog' automatically
200
- // focuses the first focusable element — no manual focus needed.
201
- if (!fg('platform-dst-top-layer')) {
202
- var _calendarRef$current;
203
- // Focus on the first button within the calendar
204
- calendarRef === null || calendarRef === void 0 || (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 || (_calendarRef$current = _calendarRef$current.querySelector('button')) === null || _calendarRef$current === void 0 || _calendarRef$current.focus();
205
- }
203
+ // Focus on the first button within the calendar
204
+ calendarRef === null || calendarRef === void 0 || (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 || (_calendarRef$current = _calendarRef$current.querySelector('button')) === null || _calendarRef$current === void 0 || _calendarRef$current.focus();
206
205
  }
207
206
  }, [isKeyDown, calendarRef, isOpen, wasOpenedFromCalendarButton]);
208
207
  var getValue = function getValue() {
@@ -249,6 +248,7 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
249
248
  var onContainerBlur = function onContainerBlur(event) {
250
249
  var _containerRef$current2;
251
250
  var newlyFocusedElement = event.relatedTarget;
251
+ isPointerInteractionRef.current = false;
252
252
  if (!(containerRef !== null && containerRef !== void 0 && (_containerRef$current2 = containerRef.current) !== null && _containerRef$current2 !== void 0 && _containerRef$current2.contains(newlyFocusedElement))) {
253
253
  setIsOpen(false);
254
254
  setShouldSetFocusOnCurrentDay(false);
@@ -279,8 +279,12 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
279
279
  // Don't open menu if focussing after the user has clicked clear
280
280
  setClearingFromIcon(false);
281
281
  } else {
282
- // Don't open when focused into via keyboard if the calendar button is present
283
- setIsOpen(!shouldShowCalendarButton);
282
+ // Opening an auto popover during pointer focus allows the matching pointerup
283
+ // to immediately light-dismiss it. Let the subsequent click open it instead.
284
+ if (!fg('platform-dst-top-layer') || !isPointerInteractionRef.current) {
285
+ // Don't open when focused into via keyboard if the calendar button is present
286
+ setIsOpen(!shouldShowCalendarButton);
287
+ }
284
288
  setCalendarValue(value);
285
289
  setIsFocused(true);
286
290
  setWasOpenedFromCalendarButton(false);
@@ -316,12 +320,7 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
316
320
  }
317
321
  switch (keyPressed) {
318
322
  case 'escape':
319
- // Restore focus on close. Both code paths handle this here:
320
- // the legacy path because it has no built-in restoration, and
321
- // the top-layer path because the menu uses `mode="manual"` to
322
- // avoid the auto-popover light-dismiss closing the menu on the
323
- // same click that opens it (see internal/menu-top-layer.tsx).
324
- // `manual` mode disables the browser's native focus return.
323
+ // Restore focus because it may have moved from the input into the calendar.
325
324
  if (wasOpenedFromCalendarButton) {
326
325
  var _calendarButtonRef$cu2;
327
326
  (_calendarButtonRef$cu2 = calendarButtonRef.current) === null || _calendarButtonRef$cu2 === void 0 || _calendarButtonRef$cu2.focus();
@@ -399,6 +398,9 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
399
398
 
400
399
  // This event handler is triggered from both keydown and click. It's weird.
401
400
  var onCalendarButtonClick = function onCalendarButtonClick(e) {
401
+ if (!getIsOpen() && fg('platform-dst-top-layer')) {
402
+ setIsKeyDown(true);
403
+ }
402
404
  setIsOpen(function (isOpen) {
403
405
  if (isOpen) {
404
406
  var _props$selectProps2, _props$selectProps2$o;
@@ -528,11 +530,28 @@ var DatePicker = /*#__PURE__*/forwardRef(function (props, _forwardedRef) {
528
530
  var openCalendarLabelId = "open-calendar-label--".concat(useId());
529
531
  return /*#__PURE__*/React.createElement("div", _extends({}, innerProps, {
530
532
  "data-testid": testId && "".concat(testId, "--container"),
531
- onBlur: onContainerBlur,
533
+ onBlur: onContainerBlur
534
+ // Reset before descendant click handlers run: click happens after native light-dismiss, so
535
+ // any focus they move is safe to treat as non-pointer focus. Capture also guarantees cleanup
536
+ // when a descendant stops the bubbling click.
537
+ ,
538
+ onClickCapture: function onClickCapture() {
539
+ isPointerInteractionRef.current = false;
540
+ },
532
541
  onFocus: onContainerFocus,
533
542
  onClick: onInputClick,
534
543
  onInput: onTextInput,
535
- onKeyDown: onInputKeyDown,
544
+ onKeyDown: onInputKeyDown
545
+ // A cancelled pointer produces no click, so clear the tracking state here instead.
546
+ ,
547
+ onPointerCancelCapture: function onPointerCancelCapture() {
548
+ isPointerInteractionRef.current = false;
549
+ }
550
+ // Capture pointerdown before it moves focus into a descendant Select control.
551
+ ,
552
+ onPointerDownCapture: function onPointerDownCapture() {
553
+ isPointerInteractionRef.current = true;
554
+ },
536
555
  ref: getContainerRef
537
556
  // Since the onclick, onfocus are passed down, adding role="presentation" prevents typecheck errors.
538
557
  ,
@@ -17,7 +17,7 @@ import React, { forwardRef, useCallback, useEffect, useReducer, useRef, useState
17
17
  // oxlint-disable-next-line @atlassian/no-restricted-imports
18
18
  import { format, isValid, parseISO } from 'date-fns';
19
19
  import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler';
20
- import { IconButton } from '@atlaskit/button/new';
20
+ import IconButton from '@atlaskit/button/icon/button';
21
21
  import SelectClearIcon from '@atlaskit/icon/core/cross-circle';
22
22
  import { Box, Inline } from '@atlaskit/primitives/compiled';
23
23
  import { mergeStyles } from '@atlaskit/react-select/styles';
@@ -27,7 +27,7 @@ import { convertTokens } from '../internal/parse-tokens';
27
27
  import DatePicker from './date-picker';
28
28
  import TimePicker from './time-picker';
29
29
  var packageName = "@atlaskit/datetime-picker";
30
- var packageVersion = "18.3.1";
30
+ var packageVersion = "18.4.1";
31
31
  var analyticsAttributes = {
32
32
  componentName: 'dateTimePicker',
33
33
  packageName: packageName,
@@ -25,7 +25,7 @@ import { convertTokens } from '../internal/parse-tokens';
25
25
  import { placeholderDatetime } from '../internal/placeholder-date-time';
26
26
  import { makeSingleValue } from '../internal/single-value';
27
27
  var packageName = "@atlaskit/datetime-picker";
28
- var packageVersion = "18.3.1";
28
+ var packageVersion = "18.4.1";
29
29
  var defaultTimeFormat = 'h:mma';
30
30
  var menuStyles = {
31
31
  /* Need to remove default absolute positioning as that causes issues with position fixed */
@@ -6,8 +6,8 @@ import * as React from 'react';
6
6
  import { ax, ix } from "@compiled/react/runtime";
7
7
  import { useRef } from 'react';
8
8
  import { components } from '@atlaskit/react-select/components';
9
- import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
10
- import { Popover } from '@atlaskit/top-layer/popover';
9
+ import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map/index';
10
+ import { Popover } from '@atlaskit/top-layer/popover/popover';
11
11
  import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
12
12
  import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
13
13
 
@@ -0,0 +1,5 @@
1
+ ._2rko1mok{border-radius:var(--ds-radius-large,8px)}._16qs130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
2
+ ._1bsb1ris{width:max-content}
3
+ ._1pfhu2gc{margin-block-start:var(--ds-space-100,8px)}
4
+ ._6rthu2gc{margin-block-end:var(--ds-space-100,8px)}
5
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
@@ -1,23 +1,11 @@
1
1
  /* menu-top-layer.tsx generated by @compiled/babel-plugin v2.0.0 */
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
+ import "./menu-top-layer.compiled.css";
3
4
  import * as React from 'react';
4
5
  import { ax, ix } from "@compiled/react/runtime";
5
- import { Fragment, useRef } from 'react';
6
+ import { Fragment } from 'react';
6
7
  import { isValid, parseISO } from 'date-fns';
7
8
  import Calendar from '@atlaskit/calendar/calendar';
8
- import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
9
- import { Popover } from '@atlaskit/top-layer/popover';
10
- import { PopoverSurface } from '@atlaskit/top-layer/popover-surface';
11
- import { useAnchorPosition } from '@atlaskit/top-layer/use-anchor-position';
12
-
13
- /**
14
- * Bottom-start placement: calendar appears below and aligned to the
15
- * start edge of the trigger (the select input).
16
- */
17
- var popupPlacement = fromLegacyPlacement({
18
- legacy: 'bottom-start'
19
- });
20
-
21
9
  /**
22
10
  * @param isos A series of ISO dates.
23
11
  * @returns The last valid date within the array of ISO strings.
@@ -32,19 +20,19 @@ function getValidDate(isos) {
32
20
  } : acc;
33
21
  }, {});
34
22
  }
23
+ var styles = {
24
+ root: "_2rko1mok _1bsb1ris _bfhk1bhr _16qs130s _1pfhu2gc _6rthu2gc"
25
+ };
35
26
 
36
27
  /**
37
28
  * Top-layer version of the date picker menu.
38
29
  *
39
- * Uses `Popover` + `useAnchorPosition` so the calendar renders in the
40
- * browser's top layer via the native Popover API and is positioned via
41
- * CSS Anchor Positioning. This avoids overflow clipping, z-index wars,
42
- * and portal-based layering.
30
+ * The surrounding react-select menu portal owns the native popover and
31
+ * positioning. This component only supplies the calendar dialog content.
43
32
  *
44
33
  * Gated behind the `platform-dst-top-layer` feature flag.
45
34
  */
46
35
  export var MenuTopLayer = function MenuTopLayer(_ref) {
47
- var _selectProps$calendar, _selectProps$calendar2;
48
36
  var selectProps = _ref.selectProps,
49
37
  innerProps = _ref.innerProps;
50
38
  var calendarValue = selectProps.calendarValue,
@@ -54,20 +42,6 @@ export var MenuTopLayer = function MenuTopLayer(_ref) {
54
42
  day = _getValidDate.day,
55
43
  month = _getValidDate.month,
56
44
  year = _getValidDate.year;
57
-
58
- // The select's container element is the anchor for the popup.
59
- var triggerRef = useRef((_selectProps$calendar = selectProps.calendarContainerRef) !== null && _selectProps$calendar !== void 0 ? _selectProps$calendar : null);
60
- triggerRef.current = (_selectProps$calendar2 = selectProps.calendarContainerRef) !== null && _selectProps$calendar2 !== void 0 ? _selectProps$calendar2 : null;
61
- var popoverRef = useRef(null);
62
- useAnchorPosition({
63
- anchorRef: triggerRef,
64
- popoverRef: popoverRef,
65
- placement: popupPlacement,
66
- // The Popover is rendered with `isOpen` always true while this
67
- // component is mounted (react-select conditionally mounts/unmounts
68
- // the menu), so positioning is always active.
69
- isOpen: true
70
- });
71
45
  var onMenuMouseDown = function onMenuMouseDown(event) {
72
46
  if (event.button !== 0) {
73
47
  return;
@@ -76,25 +50,10 @@ export var MenuTopLayer = function MenuTopLayer(_ref) {
76
50
  event.preventDefault();
77
51
  };
78
52
  var Wrapper = typeof MenuInnerWrapper === 'function' ? MenuInnerWrapper : Fragment;
79
- return /*#__PURE__*/React.createElement(Popover, {
80
- ref: popoverRef,
81
- role: "dialog",
82
- label: "calendar",
83
- isOpen: true
84
- // `mode="manual"` opts out of native popover light-dismiss.
85
- // react-select / DateTimePicker already own outside-click and
86
- // Esc handling. Without this, the same click that opens the
87
- // menu (which targets the select input outside the popover)
88
- // also bubbles to the browser's auto-dismiss handler and
89
- // immediately closes the popover.
90
- ,
91
- mode: "manual",
92
- placement: popupPlacement,
93
- shouldAnimate: true,
94
- testId: selectProps.testId && "".concat(selectProps.testId, "--popup")
95
- }, /*#__PURE__*/React.createElement(PopoverSurface, null, /*#__PURE__*/React.createElement("div", _extends({}, innerProps, {
53
+ return /*#__PURE__*/React.createElement("div", _extends({}, innerProps, {
54
+ onMouseDown: onMenuMouseDown,
96
55
  role: "presentation",
97
- onMouseDown: onMenuMouseDown
56
+ className: ax([styles.root])
98
57
  }), /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Calendar, {
99
58
  day: day,
100
59
  month: month,
@@ -113,5 +72,5 @@ export var MenuTopLayer = function MenuTopLayer(_ref) {
113
72
  locale: selectProps.calendarLocale,
114
73
  testId: selectProps.testId && "".concat(selectProps.testId, "--calendar"),
115
74
  weekStartDay: selectProps.calendarWeekStartDay
116
- })))));
75
+ })));
117
76
  };
@@ -6,10 +6,8 @@ import type { MenuProps, OptionType } from '@atlaskit/select/types';
6
6
  /**
7
7
  * Top-layer version of the date picker menu.
8
8
  *
9
- * Uses `Popover` + `useAnchorPosition` so the calendar renders in the
10
- * browser's top layer via the native Popover API and is positioned via
11
- * CSS Anchor Positioning. This avoids overflow clipping, z-index wars,
12
- * and portal-based layering.
9
+ * The surrounding react-select menu portal owns the native popover and
10
+ * positioning. This component only supplies the calendar dialog content.
13
11
  *
14
12
  * Gated behind the `platform-dst-top-layer` feature flag.
15
13
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/datetime-picker",
3
- "version": "18.4.0",
3
+ "version": "18.4.2",
4
4
  "description": "A date time picker allows the user to select an associated date and time.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -31,10 +31,10 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@atlaskit/analytics-next": "^12.3.0",
34
+ "@atlaskit/analytics-next": "^12.4.0",
35
35
  "@atlaskit/button": "^25.2.0",
36
36
  "@atlaskit/calendar": "^19.1.0",
37
- "@atlaskit/css": "^1.0.0",
37
+ "@atlaskit/css": "^1.1.0",
38
38
  "@atlaskit/ds-lib": "^8.0.0",
39
39
  "@atlaskit/icon": "^37.5.0",
40
40
  "@atlaskit/layering": "^5.1.0",
@@ -43,16 +43,16 @@
43
43
  "@atlaskit/popper": "^9.3.0",
44
44
  "@atlaskit/primitives": "^22.4.0",
45
45
  "@atlaskit/react-compiler-gating": "^0.2.0",
46
- "@atlaskit/react-select": "^4.8.0",
47
- "@atlaskit/select": "^22.8.0",
48
- "@atlaskit/tokens": "^16.8.0",
46
+ "@atlaskit/react-select": "^4.9.0",
47
+ "@atlaskit/select": "^22.10.0",
48
+ "@atlaskit/tokens": "^16.9.0",
49
49
  "@atlaskit/top-layer": "^4.4.0",
50
50
  "@babel/runtime": "^7.0.0",
51
51
  "@compiled/react": "^1.0.2",
52
52
  "date-fns": "^2.17.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "react": "^18.2.0"
55
+ "react": "^18.2.0 || ^19.0.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@af/accessibility-testing": "workspace:^",
@@ -79,7 +79,7 @@
79
79
  "jest-in-case": "^1.0.2",
80
80
  "jscodeshift": "^17.0.0",
81
81
  "moment": "^2.29.2",
82
- "react": "^18.2.0",
82
+ "react": "^19.0.0",
83
83
  "react-dom": "^18.2.0",
84
84
  "react-lorem-component": "^0.13.0"
85
85
  },