@atlaskit/navigation-system 5.23.0 → 5.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/ui/menu-item/flyout-menu-item/flyout-header.js +6 -7
  3. package/dist/cjs/ui/menu-item/flyout-menu-item/flyout-menu-item-content.js +43 -7
  4. package/dist/cjs/ui/menu-item/flyout-menu-item/flyout-menu-item-context.js +7 -10
  5. package/dist/cjs/ui/menu-item/flyout-menu-item/flyout-menu-item-trigger.js +15 -1
  6. package/dist/cjs/ui/menu-item/flyout-menu-item/flyout-menu-item.js +20 -2
  7. package/dist/cjs/ui/page-layout/side-nav/side-nav.js +1 -20
  8. package/dist/cjs/ui/page-layout/side-nav/toggle-button.js +1 -1
  9. package/dist/cjs/ui/page-layout/side-nav/visibility-provider.js +1 -2
  10. package/dist/es2019/ui/menu-item/flyout-menu-item/close-button.js +2 -2
  11. package/dist/es2019/ui/menu-item/flyout-menu-item/flyout-header.js +7 -8
  12. package/dist/es2019/ui/menu-item/flyout-menu-item/flyout-menu-item-content.js +45 -7
  13. package/dist/es2019/ui/menu-item/flyout-menu-item/flyout-menu-item-context.js +6 -10
  14. package/dist/es2019/ui/menu-item/flyout-menu-item/flyout-menu-item-trigger.js +45 -29
  15. package/dist/es2019/ui/menu-item/flyout-menu-item/flyout-menu-item.js +23 -4
  16. package/dist/es2019/ui/page-layout/side-nav/side-nav.js +1 -20
  17. package/dist/es2019/ui/page-layout/side-nav/toggle-button.js +1 -1
  18. package/dist/es2019/ui/page-layout/side-nav/visibility-provider.js +1 -2
  19. package/dist/esm/ui/menu-item/flyout-menu-item/close-button.js +2 -2
  20. package/dist/esm/ui/menu-item/flyout-menu-item/flyout-header.js +7 -8
  21. package/dist/esm/ui/menu-item/flyout-menu-item/flyout-menu-item-content.js +44 -9
  22. package/dist/esm/ui/menu-item/flyout-menu-item/flyout-menu-item-context.js +6 -10
  23. package/dist/esm/ui/menu-item/flyout-menu-item/flyout-menu-item-trigger.js +16 -2
  24. package/dist/esm/ui/menu-item/flyout-menu-item/flyout-menu-item.js +22 -4
  25. package/dist/esm/ui/page-layout/side-nav/side-nav.js +1 -20
  26. package/dist/esm/ui/page-layout/side-nav/toggle-button.js +1 -1
  27. package/dist/esm/ui/page-layout/side-nav/visibility-provider.js +1 -2
  28. package/dist/types/ui/menu-item/flyout-menu-item/close-button.d.ts +1 -1
  29. package/dist/types/ui/menu-item/flyout-menu-item/flyout-menu-item-content.d.ts +1 -0
  30. package/dist/types/ui/menu-item/flyout-menu-item/flyout-menu-item-context.d.ts +5 -8
  31. package/dist/types-ts4.5/ui/menu-item/flyout-menu-item/close-button.d.ts +1 -1
  32. package/dist/types-ts4.5/ui/menu-item/flyout-menu-item/flyout-menu-item-content.d.ts +1 -0
  33. package/dist/types-ts4.5/ui/menu-item/flyout-menu-item/flyout-menu-item-context.d.ts +5 -8
  34. package/package.json +4 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlassian/navigation-system
2
2
 
3
+ ## 5.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`e7b6c5d52b32e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e7b6c5d52b32e) -
8
+ Adds analytics tracking for flyout menu opening and closing, including the close source (close
9
+ button, escape key, outside click, other). These additional analytics are behind the
10
+ `platform_dst_nav4_flyout_menu_slots_close_button` feature gate.
11
+
12
+ ## 5.24.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`2fafe4842f106`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2fafe4842f106) -
17
+ Cleans up the `platform_dst_nav4_side_nav_default_collapsed_api` feature gate. Default side nav
18
+ collapsed state can now be passed into the `Root` component via the `defaultSideNavCollapsed`
19
+ prop. This is the preferred API, and the legacy API will be removed at some point in the future.
20
+
21
+ This was previously reverted.
22
+
3
23
  ## 5.23.0
4
24
 
5
25
  ### Minor Changes
@@ -32,12 +32,11 @@ var FlyoutHeader = exports.FlyoutHeader = function FlyoutHeader(props) {
32
32
  title = props.title,
33
33
  closeButtonLabel = props.closeButtonLabel;
34
34
  var id = (0, _flyoutMenuItemContext.useTitleId)();
35
- var setIsOpen = (0, _react.useContext)(_flyoutMenuItemContext.SetIsOpenContext);
36
- var onClose = (0, _react.useContext)(_flyoutMenuItemContext.OnCloseContext);
37
- var handleClose = (0, _react.useCallback)(function () {
38
- onClose === null || onClose === void 0 || onClose();
39
- setIsOpen(false);
40
- }, [setIsOpen, onClose]);
35
+ var onCloseRef = (0, _react.useContext)(_flyoutMenuItemContext.OnCloseContext);
36
+ var handleClose = (0, _react.useCallback)(function (event) {
37
+ var _onCloseRef$current;
38
+ (_onCloseRef$current = onCloseRef.current) === null || _onCloseRef$current === void 0 || _onCloseRef$current.call(onCloseRef, event, 'close-button');
39
+ }, [onCloseRef]);
41
40
  return /*#__PURE__*/_react.default.createElement("div", {
42
41
  "data-testid": testId,
43
42
  className: (0, _runtime.ax)([headerStyles.root])
@@ -49,7 +48,7 @@ var FlyoutHeader = exports.FlyoutHeader = function FlyoutHeader(props) {
49
48
  testId: testId && "".concat(testId, "--close-button")
50
49
  }), /*#__PURE__*/_react.default.createElement(_heading.default, {
51
50
  size: "xsmall",
52
- as: "span",
51
+ as: "h2",
53
52
  id: id
54
53
  }, title)), children);
55
54
  };
@@ -11,6 +11,7 @@ require("./flyout-menu-item-content.compiled.css");
11
11
  var _runtime = require("@compiled/react/runtime");
12
12
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
13
  var _react = _interopRequireWildcard(require("react"));
14
+ var _analyticsNext = require("@atlaskit/analytics-next");
14
15
  var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
15
16
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
16
17
  var _experimental = require("@atlaskit/popup/experimental");
@@ -46,10 +47,49 @@ var FlyoutMenuItemContent = exports.FlyoutMenuItemContent = /*#__PURE__*/(0, _re
46
47
  onClose = _ref.onClose,
47
48
  autoFocus = _ref.autoFocus;
48
49
  var setIsOpen = (0, _react.useContext)(_flyoutMenuItemContext.SetIsOpenContext);
49
- var handleClose = (0, _react.useCallback)(function () {
50
+ var onCloseRef = (0, _react.useContext)(_flyoutMenuItemContext.OnCloseContext);
51
+ var _useAnalyticsEvents = (0, _analyticsNext.useAnalyticsEvents)(),
52
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
53
+
54
+ // The source of the close is not accessible to the consumer, it is determined within the
55
+ // handleClose function, or passed in as a parameter in FlyoutMenuItemTrigger (outside-click),
56
+ // or FlyoutHeader (close-button).
57
+ var handleClose = (0, _react.useCallback)(function (event, source) {
58
+ if ((0, _platformFeatureFlags.fg)("platform_dst_nav4_flyout_menu_slots_close_button")) {
59
+ // Use the passed source if provided, otherwise determine from event
60
+ var determinedSource = source || 'other';
61
+ if (!source) {
62
+ if (event instanceof KeyboardEvent) {
63
+ var keyboardEvent = event;
64
+ if (keyboardEvent.key === 'Escape' || keyboardEvent.key === 'Esc') {
65
+ determinedSource = 'escape-key';
66
+ }
67
+ } else if (event instanceof MouseEvent) {
68
+ if (event && 'type' in event && event.type === 'click') {
69
+ determinedSource = 'outside-click';
70
+ }
71
+ }
72
+ }
73
+
74
+ // When flyout menu is closed, fire analytics event
75
+ var navigationAnalyticsEvent = createAnalyticsEvent({
76
+ source: 'sideNav',
77
+ actionSubject: 'flyoutMenu',
78
+ action: 'closed',
79
+ attributes: {
80
+ closeSource: determinedSource
81
+ }
82
+ });
83
+ navigationAnalyticsEvent.fire('navigation');
84
+ }
50
85
  onClose === null || onClose === void 0 || onClose();
51
86
  setIsOpen(false);
52
- }, [setIsOpen, onClose]);
87
+ }, [setIsOpen, onClose, createAnalyticsEvent]);
88
+
89
+ // Register handleClose in the ref to allow the FlyoutMenuItemTrigger to access it
90
+ (0, _react.useEffect)(function () {
91
+ onCloseRef.current = handleClose;
92
+ }, [handleClose, onCloseRef]);
53
93
  var titleId = (0, _react.useId)();
54
94
  return /*#__PURE__*/_react.default.createElement(_experimental.PopupContent, {
55
95
  appearance: "UNSAFE_modal-below-sm",
@@ -88,13 +128,9 @@ var FlyoutMenuItemContent = exports.FlyoutMenuItemContent = /*#__PURE__*/(0, _re
88
128
  update: update
89
129
  }, (0, _platformFeatureFlags.fg)("platform_dst_nav4_flyout_menu_slots_close_button") ? /*#__PURE__*/_react.default.createElement(_flyoutMenuItemContext.TitleIdContextProvider, {
90
130
  value: titleId
91
- }, /*#__PURE__*/_react.default.createElement(_flyoutMenuItemContext.OnCloseProvider, {
92
- value: function value() {
93
- return onClose;
94
- }
95
131
  }, /*#__PURE__*/_react.default.createElement("div", {
96
132
  className: (0, _runtime.ax)([flyoutMenuItemContentContainerStyles.container])
97
- }, children))) : children);
133
+ }, children)) : children);
98
134
  });
99
135
  });
100
136
  function createResizeObserver(update) {
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.useTitleId = exports.useSetFlyoutMenuOpen = exports.useFlyoutMenuOpen = exports.TitleIdContextProvider = exports.TitleIdContext = exports.SetIsOpenContext = exports.OnCloseProvider = exports.OnCloseContext = exports.IsOpenContext = void 0;
7
+ exports.useTitleId = exports.useSetFlyoutMenuOpen = exports.useFlyoutMenuOpen = exports.TitleIdContextProvider = exports.TitleIdContext = exports.SetIsOpenContext = exports.OnCloseContext = exports.IsOpenContext = void 0;
8
8
  var _react = require("react");
9
9
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
10
10
  /**
@@ -30,16 +30,13 @@ var useSetFlyoutMenuOpen = exports.useSetFlyoutMenuOpen = function useSetFlyoutM
30
30
  /**
31
31
  * __On close context__
32
32
  *
33
- * A context for storing the onClose value of the FlyoutMenuItem.
33
+ * A context for storing a ref to the onClose handler with source information.This
34
+ * is used by FlyoutMenuItemContent, FlyoutMenuItemTrigger and FlyoutHeader to store
35
+ * the on close function and source information for closing the flyout menu.
34
36
  */
35
- var OnCloseContext = exports.OnCloseContext = /*#__PURE__*/(0, _react.createContext)(null);
36
-
37
- /**
38
- * __On close provider__
39
- *
40
- * A context provider for supplying the onClose function to the FlyoutHeader.
41
- */
42
- var OnCloseProvider = exports.OnCloseProvider = OnCloseContext.Provider;
37
+ var OnCloseContext = exports.OnCloseContext = /*#__PURE__*/(0, _react.createContext)({
38
+ current: null
39
+ });
43
40
 
44
41
  /**
45
42
  * __Title id context__
@@ -13,8 +13,10 @@ var React = _react;
13
13
  var _runtime = require("@compiled/react/runtime");
14
14
  var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
15
15
  var _chevronRight = _interopRequireDefault(require("@atlaskit/icon/core/chevron-right"));
16
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
16
17
  var _experimental = require("@atlaskit/popup/experimental");
17
18
  var _menuItem = require("../menu-item");
19
+ var _flyoutMenuItemContext = require("./flyout-menu-item-context");
18
20
  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); }
19
21
  var elemAfterStyles = {
20
22
  root: "_18zr12x7 _1tz3r0mg"
@@ -36,6 +38,18 @@ var FlyoutMenuItemTrigger = exports.FlyoutMenuItemTrigger = /*#__PURE__*/(0, _re
36
38
  isDragging = _ref.isDragging,
37
39
  hasDragIndicator = _ref.hasDragIndicator,
38
40
  dropIndicator = _ref.dropIndicator;
41
+ var isOpen = (0, _react.useContext)(_flyoutMenuItemContext.IsOpenContext);
42
+ var onCloseRef = (0, _react.useContext)(_flyoutMenuItemContext.OnCloseContext);
43
+ var handleClick = (0, _react.useCallback)(function (event, analyticsEvent) {
44
+ // If the flyout is open and the trigger is clicked, close the flyout and call the onClick
45
+ // handler with the source information set to 'outside-click'.
46
+ if ((0, _platformFeatureFlags.fg)('platform_dst_nav4_flyout_menu_slots_close_button')) {
47
+ if (isOpen && onCloseRef.current) {
48
+ onCloseRef.current(event, 'outside-click');
49
+ }
50
+ }
51
+ onClick === null || onClick === void 0 || onClick(event, analyticsEvent);
52
+ }, [isOpen, onCloseRef, onClick]);
39
53
  return /*#__PURE__*/React.createElement(_experimental.PopupTrigger, null, function (_ref2) {
40
54
  var ref = _ref2.ref,
41
55
  ariaControls = _ref2['aria-controls'],
@@ -53,7 +67,7 @@ var FlyoutMenuItemTrigger = exports.FlyoutMenuItemTrigger = /*#__PURE__*/(0, _re
53
67
  color: "currentColor",
54
68
  size: "small"
55
69
  })),
56
- onClick: onClick,
70
+ onClick: handleClick,
57
71
  ariaControls: ariaControls,
58
72
  ariaExpanded: ariaExpanded,
59
73
  ariaHasPopup: ariaHasPopup,
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.FlyoutMenuItem = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
+ var _analyticsNext = require("@atlaskit/analytics-next");
11
12
  var _useControlled3 = _interopRequireDefault(require("@atlaskit/ds-lib/use-controlled"));
12
13
  var _usePreviousValue = _interopRequireDefault(require("@atlaskit/ds-lib/use-previous-value"));
13
14
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
@@ -49,6 +50,9 @@ var FlyoutMenuItem = exports.FlyoutMenuItem = /*#__PURE__*/(0, _react.forwardRef
49
50
  isOpen = _useControlled2[0],
50
51
  setIsOpen = _useControlled2[1];
51
52
  var previousIsOpen = (0, _usePreviousValue.default)(isOpen);
53
+ var onCloseRef = (0, _react.useRef)(null);
54
+ var _useAnalyticsEvents = (0, _analyticsNext.useAnalyticsEvents)(),
55
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
52
56
  (0, _react.useEffect)(function () {
53
57
  if (previousIsOpen === undefined || previousIsOpen === isOpen) {
54
58
  /**
@@ -60,17 +64,31 @@ var FlyoutMenuItem = exports.FlyoutMenuItem = /*#__PURE__*/(0, _react.forwardRef
60
64
  */
61
65
  return;
62
66
  }
67
+
68
+ // When flyout menu is opened, fire analytics event
69
+ if (isOpen && previousIsOpen === false) {
70
+ if ((0, _platformFeatureFlags.fg)('platform_dst_nav4_flyout_menu_slots_close_button')) {
71
+ var navigationAnalyticsEvent = createAnalyticsEvent({
72
+ source: 'sideNav',
73
+ actionSubject: 'flyoutMenu',
74
+ action: 'opened'
75
+ });
76
+ navigationAnalyticsEvent.fire('navigation');
77
+ }
78
+ }
63
79
  onOpenChange === null || onOpenChange === void 0 || onOpenChange(isOpen);
64
- }, [isOpen, onOpenChange, previousIsOpen]);
80
+ }, [isOpen, onOpenChange, previousIsOpen, createAnalyticsEvent]);
65
81
  return /*#__PURE__*/_react.default.createElement(_flyoutMenuItemContext.IsOpenContext.Provider, {
66
82
  value: isOpen
67
83
  }, /*#__PURE__*/_react.default.createElement(_flyoutMenuItemContext.SetIsOpenContext.Provider, {
68
84
  value: setIsOpen
85
+ }, /*#__PURE__*/_react.default.createElement(_flyoutMenuItemContext.OnCloseContext.Provider, {
86
+ value: onCloseRef
69
87
  }, /*#__PURE__*/_react.default.createElement(_menuListItem.MenuListItem, {
70
88
  ref: forwardedRef
71
89
  }, /*#__PURE__*/_react.default.createElement(_experimental.Popup, {
72
90
  id: id,
73
91
  isOpen: isOpen,
74
92
  role: (0, _platformFeatureFlags.fg)('platform_dst_nav4_flyout_menu_slots_close_button') ? 'dialog' : undefined
75
- }, children))));
93
+ }, children)))));
76
94
  });
@@ -344,29 +344,10 @@ function SideNavInternal(_ref) {
344
344
  var toggleVisibilityByClickOutsideOnMobile = (0, _useToggleSideNav.useToggleSideNav)({
345
345
  trigger: 'click-outside-on-mobile'
346
346
  });
347
- (0, _react.useEffect)(function () {
348
- if ((0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api')) {
349
- // This is the old version of the hook, so we skip it when the flag is enabled
350
- return;
351
- }
352
-
353
- // Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
354
- // after SSR hydration. This should only run once, after the initial render on the client.
355
- setSideNavState({
356
- desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
357
- mobile: 'collapsed',
358
- flyout: 'closed',
359
- lastTrigger: null
360
- });
361
- }, [initialDefaultCollapsed, setSideNavState]);
362
347
 
363
- // Moving to `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
348
+ // Using `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
364
349
  // For SSR the new API is still necessary
365
350
  (0, _react.useLayoutEffect)(function () {
366
- if (!(0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api')) {
367
- // This is the new version of the hook, so we skip it when the flag is disabled
368
- return;
369
- }
370
351
  if (sideNavState !== null) {
371
352
  // Only need to do an initial sync if it hasn't been initialized from Root
372
353
  return;
@@ -61,7 +61,7 @@ var SideNavToggleButton = exports.SideNavToggleButton = function SideNavToggleBu
61
61
 
62
62
  // When default state is provided to `Root` the state in context will already be
63
63
  // initialized in SSR
64
- var _useState = (0, _react.useState)(sideNavState === null || !(0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api') ? !defaultCollapsed : isSideNavExpandedOnDesktop),
64
+ var _useState = (0, _react.useState)(sideNavState === null ? !defaultCollapsed : isSideNavExpandedOnDesktop),
65
65
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
66
66
  isSideNavExpanded = _useState2[0],
67
67
  setIsSideNavExpanded = _useState2[1];
@@ -8,7 +8,6 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.SideNavVisibilityProvider = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
- var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
11
  var _visibilityContext = require("./visibility-context");
13
12
  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); }
14
13
  /**
@@ -17,7 +16,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
17
16
  var SideNavVisibilityProvider = exports.SideNavVisibilityProvider = function SideNavVisibilityProvider(_ref) {
18
17
  var children = _ref.children,
19
18
  defaultCollapsed = _ref.defaultCollapsed;
20
- var initialState = typeof defaultCollapsed === 'boolean' && (0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api') ? {
19
+ var initialState = typeof defaultCollapsed === 'boolean' ? {
21
20
  desktop: defaultCollapsed ? 'collapsed' : 'expanded',
22
21
  mobile: 'collapsed',
23
22
  flyout: 'closed',
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { IconButton } from "@atlaskit/button/new";
3
- import CrossIcon from "@atlaskit/icon/core/cross";
2
+ import { IconButton } from '@atlaskit/button/new';
3
+ import CrossIcon from '@atlaskit/icon/core/cross';
4
4
  /**
5
5
  * __Close button__
6
6
  *
@@ -5,7 +5,7 @@ import React, { useCallback, useContext } from 'react';
5
5
  import Heading from '@atlaskit/heading';
6
6
  import { Flex } from '@atlaskit/primitives/compiled';
7
7
  import { CloseButton } from './close-button';
8
- import { OnCloseContext, SetIsOpenContext, useTitleId } from './flyout-menu-item-context';
8
+ import { OnCloseContext, useTitleId } from './flyout-menu-item-context';
9
9
  const headerStyles = {
10
10
  root: "_zulp12x7 _bozg1b66 _85i512x7 _1e0c1txw _2lx21bp4",
11
11
  flex: "_zulppxbi _1bah1yb4 _2lx21sbv _4cvr1h6o _1bsb1osq _bozgv77o"
@@ -25,12 +25,11 @@ export const FlyoutHeader = props => {
25
25
  closeButtonLabel
26
26
  } = props;
27
27
  const id = useTitleId();
28
- const setIsOpen = useContext(SetIsOpenContext);
29
- const onClose = useContext(OnCloseContext);
30
- const handleClose = useCallback(() => {
31
- onClose === null || onClose === void 0 ? void 0 : onClose();
32
- setIsOpen(false);
33
- }, [setIsOpen, onClose]);
28
+ const onCloseRef = useContext(OnCloseContext);
29
+ const handleClose = useCallback(event => {
30
+ var _onCloseRef$current;
31
+ (_onCloseRef$current = onCloseRef.current) === null || _onCloseRef$current === void 0 ? void 0 : _onCloseRef$current.call(onCloseRef, event, 'close-button');
32
+ }, [onCloseRef]);
34
33
  return /*#__PURE__*/React.createElement("div", {
35
34
  "data-testid": testId,
36
35
  className: ax([headerStyles.root])
@@ -42,7 +41,7 @@ export const FlyoutHeader = props => {
42
41
  testId: testId && `${testId}--close-button`
43
42
  }), /*#__PURE__*/React.createElement(Heading, {
44
43
  size: "xsmall",
45
- as: "span",
44
+ as: "h2",
46
45
  id: id
47
46
  }, title)), children);
48
47
  };
@@ -2,11 +2,11 @@
2
2
  import "./flyout-menu-item-content.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React, { forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
5
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
5
6
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
6
7
  import { fg } from '@atlaskit/platform-feature-flags';
7
8
  import { PopupContent } from '@atlaskit/popup/experimental';
8
- import { OnCloseProvider, SetIsOpenContext, TitleIdContextProvider } from './flyout-menu-item-context';
9
-
9
+ import { OnCloseContext, SetIsOpenContext, TitleIdContextProvider } from './flyout-menu-item-context';
10
10
  /**
11
11
  * The vertical offset in px to ensure the flyout container does not exceed the bounds of
12
12
  * the window. This matches the padding of the content container, and it's position within
@@ -38,10 +38,50 @@ export const FlyoutMenuItemContent = /*#__PURE__*/forwardRef(({
38
38
  autoFocus
39
39
  }, forwardedRef) => {
40
40
  const setIsOpen = useContext(SetIsOpenContext);
41
- const handleClose = useCallback(() => {
41
+ const onCloseRef = useContext(OnCloseContext);
42
+ const {
43
+ createAnalyticsEvent
44
+ } = useAnalyticsEvents();
45
+
46
+ // The source of the close is not accessible to the consumer, it is determined within the
47
+ // handleClose function, or passed in as a parameter in FlyoutMenuItemTrigger (outside-click),
48
+ // or FlyoutHeader (close-button).
49
+ const handleClose = useCallback((event, source) => {
50
+ if (fg("platform_dst_nav4_flyout_menu_slots_close_button")) {
51
+ // Use the passed source if provided, otherwise determine from event
52
+ let determinedSource = source || 'other';
53
+ if (!source) {
54
+ if (event instanceof KeyboardEvent) {
55
+ const keyboardEvent = event;
56
+ if (keyboardEvent.key === 'Escape' || keyboardEvent.key === 'Esc') {
57
+ determinedSource = 'escape-key';
58
+ }
59
+ } else if (event instanceof MouseEvent) {
60
+ if (event && 'type' in event && event.type === 'click') {
61
+ determinedSource = 'outside-click';
62
+ }
63
+ }
64
+ }
65
+
66
+ // When flyout menu is closed, fire analytics event
67
+ const navigationAnalyticsEvent = createAnalyticsEvent({
68
+ source: 'sideNav',
69
+ actionSubject: 'flyoutMenu',
70
+ action: 'closed',
71
+ attributes: {
72
+ closeSource: determinedSource
73
+ }
74
+ });
75
+ navigationAnalyticsEvent.fire('navigation');
76
+ }
42
77
  onClose === null || onClose === void 0 ? void 0 : onClose();
43
78
  setIsOpen(false);
44
- }, [setIsOpen, onClose]);
79
+ }, [setIsOpen, onClose, createAnalyticsEvent]);
80
+
81
+ // Register handleClose in the ref to allow the FlyoutMenuItemTrigger to access it
82
+ useEffect(() => {
83
+ onCloseRef.current = handleClose;
84
+ }, [handleClose, onCloseRef]);
45
85
  const titleId = useId();
46
86
  return /*#__PURE__*/React.createElement(PopupContent, {
47
87
  appearance: "UNSAFE_modal-below-sm",
@@ -80,11 +120,9 @@ export const FlyoutMenuItemContent = /*#__PURE__*/forwardRef(({
80
120
  update: update
81
121
  }, fg("platform_dst_nav4_flyout_menu_slots_close_button") ? /*#__PURE__*/React.createElement(TitleIdContextProvider, {
82
122
  value: titleId
83
- }, /*#__PURE__*/React.createElement(OnCloseProvider, {
84
- value: () => onClose
85
123
  }, /*#__PURE__*/React.createElement("div", {
86
124
  className: ax([flyoutMenuItemContentContainerStyles.container])
87
- }, children))) : children));
125
+ }, children)) : children));
88
126
  });
89
127
  function createResizeObserver(update) {
90
128
  return new ResizeObserver(update);
@@ -1,6 +1,5 @@
1
1
  import { createContext, useContext } from 'react';
2
2
  import noop from '@atlaskit/ds-lib/noop';
3
-
4
3
  /**
5
4
  * __Is open context__
6
5
  *
@@ -20,16 +19,13 @@ export const useSetFlyoutMenuOpen = () => useContext(SetIsOpenContext);
20
19
  /**
21
20
  * __On close context__
22
21
  *
23
- * A context for storing the onClose value of the FlyoutMenuItem.
24
- */
25
- export const OnCloseContext = /*#__PURE__*/createContext(null);
26
-
27
- /**
28
- * __On close provider__
29
- *
30
- * A context provider for supplying the onClose function to the FlyoutHeader.
22
+ * A context for storing a ref to the onClose handler with source information.This
23
+ * is used by FlyoutMenuItemContent, FlyoutMenuItemTrigger and FlyoutHeader to store
24
+ * the on close function and source information for closing the flyout menu.
31
25
  */
32
- export const OnCloseProvider = OnCloseContext.Provider;
26
+ export const OnCloseContext = /*#__PURE__*/createContext({
27
+ current: null
28
+ });
33
29
 
34
30
  /**
35
31
  * __Title id context__
@@ -2,11 +2,13 @@
2
2
  import "./flyout-menu-item-trigger.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
- import { forwardRef } from 'react';
5
+ import { forwardRef, useCallback, useContext } from 'react';
6
6
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
7
7
  import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
8
+ import { fg } from '@atlaskit/platform-feature-flags';
8
9
  import { PopupTrigger } from '@atlaskit/popup/experimental';
9
10
  import { MenuItemBase } from '../menu-item';
11
+ import { IsOpenContext, OnCloseContext } from './flyout-menu-item-context';
10
12
  const elemAfterStyles = {
11
13
  root: "_18zr12x7 _1tz3r0mg"
12
14
  };
@@ -27,31 +29,45 @@ export const FlyoutMenuItemTrigger = /*#__PURE__*/forwardRef(({
27
29
  isDragging,
28
30
  hasDragIndicator,
29
31
  dropIndicator
30
- }, forwardedRef) => /*#__PURE__*/React.createElement(PopupTrigger, null, ({
31
- ref,
32
- 'aria-controls': ariaControls,
33
- 'aria-expanded': ariaExpanded,
34
- 'aria-haspopup': ariaHasPopup
35
- }) => /*#__PURE__*/React.createElement(MenuItemBase, {
36
- testId: testId,
37
- ref: mergeRefs([ref, forwardedRef]),
38
- visualContentRef: visualContentRef,
39
- elemBefore: elemBefore,
40
- elemAfter: /*#__PURE__*/React.createElement("div", {
41
- className: ax([elemAfterStyles.root])
42
- }, /*#__PURE__*/React.createElement(ChevronRightIcon, {
43
- label: "",
44
- color: "currentColor",
45
- size: "small"
46
- })),
47
- onClick: onClick,
48
- ariaControls: ariaControls,
49
- ariaExpanded: ariaExpanded,
50
- ariaHasPopup: ariaHasPopup,
51
- interactionName: interactionName,
52
- isContentTooltipDisabled: isContentTooltipDisabled,
53
- isSelected: isSelected,
54
- isDragging: isDragging,
55
- hasDragIndicator: hasDragIndicator,
56
- dropIndicator: dropIndicator
57
- }, children)));
32
+ }, forwardedRef) => {
33
+ const isOpen = useContext(IsOpenContext);
34
+ const onCloseRef = useContext(OnCloseContext);
35
+ const handleClick = useCallback((event, analyticsEvent) => {
36
+ // If the flyout is open and the trigger is clicked, close the flyout and call the onClick
37
+ // handler with the source information set to 'outside-click'.
38
+ if (fg('platform_dst_nav4_flyout_menu_slots_close_button')) {
39
+ if (isOpen && onCloseRef.current) {
40
+ onCloseRef.current(event, 'outside-click');
41
+ }
42
+ }
43
+ onClick === null || onClick === void 0 ? void 0 : onClick(event, analyticsEvent);
44
+ }, [isOpen, onCloseRef, onClick]);
45
+ return /*#__PURE__*/React.createElement(PopupTrigger, null, ({
46
+ ref,
47
+ 'aria-controls': ariaControls,
48
+ 'aria-expanded': ariaExpanded,
49
+ 'aria-haspopup': ariaHasPopup
50
+ }) => /*#__PURE__*/React.createElement(MenuItemBase, {
51
+ testId: testId,
52
+ ref: mergeRefs([ref, forwardedRef]),
53
+ visualContentRef: visualContentRef,
54
+ elemBefore: elemBefore,
55
+ elemAfter: /*#__PURE__*/React.createElement("div", {
56
+ className: ax([elemAfterStyles.root])
57
+ }, /*#__PURE__*/React.createElement(ChevronRightIcon, {
58
+ label: "",
59
+ color: "currentColor",
60
+ size: "small"
61
+ })),
62
+ onClick: handleClick,
63
+ ariaControls: ariaControls,
64
+ ariaExpanded: ariaExpanded,
65
+ ariaHasPopup: ariaHasPopup,
66
+ interactionName: interactionName,
67
+ isContentTooltipDisabled: isContentTooltipDisabled,
68
+ isSelected: isSelected,
69
+ isDragging: isDragging,
70
+ hasDragIndicator: hasDragIndicator,
71
+ dropIndicator: dropIndicator
72
+ }, children));
73
+ });
@@ -1,10 +1,11 @@
1
- import React, { forwardRef, useEffect } from 'react';
1
+ import React, { forwardRef, useEffect, useRef } from 'react';
2
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
2
3
  import useControlled from '@atlaskit/ds-lib/use-controlled';
3
4
  import usePreviousValue from '@atlaskit/ds-lib/use-previous-value';
4
5
  import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { Popup } from '@atlaskit/popup/experimental';
6
7
  import { MenuListItem } from '../menu-list-item';
7
- import { IsOpenContext, SetIsOpenContext } from './flyout-menu-item-context';
8
+ import { IsOpenContext, OnCloseContext, SetIsOpenContext } from './flyout-menu-item-context';
8
9
  /**
9
10
  * __FlyoutMenuItem__
10
11
  *
@@ -34,6 +35,10 @@ export const FlyoutMenuItem = /*#__PURE__*/forwardRef(({
34
35
  }, forwardedRef) => {
35
36
  const [isOpen, setIsOpen] = useControlled(isOpenControlled, () => isDefaultOpen);
36
37
  const previousIsOpen = usePreviousValue(isOpen);
38
+ const onCloseRef = useRef(null);
39
+ const {
40
+ createAnalyticsEvent
41
+ } = useAnalyticsEvents();
37
42
  useEffect(() => {
38
43
  if (previousIsOpen === undefined || previousIsOpen === isOpen) {
39
44
  /**
@@ -45,17 +50,31 @@ export const FlyoutMenuItem = /*#__PURE__*/forwardRef(({
45
50
  */
46
51
  return;
47
52
  }
53
+
54
+ // When flyout menu is opened, fire analytics event
55
+ if (isOpen && previousIsOpen === false) {
56
+ if (fg('platform_dst_nav4_flyout_menu_slots_close_button')) {
57
+ const navigationAnalyticsEvent = createAnalyticsEvent({
58
+ source: 'sideNav',
59
+ actionSubject: 'flyoutMenu',
60
+ action: 'opened'
61
+ });
62
+ navigationAnalyticsEvent.fire('navigation');
63
+ }
64
+ }
48
65
  onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(isOpen);
49
- }, [isOpen, onOpenChange, previousIsOpen]);
66
+ }, [isOpen, onOpenChange, previousIsOpen, createAnalyticsEvent]);
50
67
  return /*#__PURE__*/React.createElement(IsOpenContext.Provider, {
51
68
  value: isOpen
52
69
  }, /*#__PURE__*/React.createElement(SetIsOpenContext.Provider, {
53
70
  value: setIsOpen
71
+ }, /*#__PURE__*/React.createElement(OnCloseContext.Provider, {
72
+ value: onCloseRef
54
73
  }, /*#__PURE__*/React.createElement(MenuListItem, {
55
74
  ref: forwardedRef
56
75
  }, /*#__PURE__*/React.createElement(Popup, {
57
76
  id: id,
58
77
  isOpen: isOpen,
59
78
  role: fg('platform_dst_nav4_flyout_menu_slots_close_button') ? 'dialog' : undefined
60
- }, children))));
79
+ }, children)))));
61
80
  });
@@ -325,29 +325,10 @@ function SideNavInternal({
325
325
  const toggleVisibilityByClickOutsideOnMobile = useToggleSideNav({
326
326
  trigger: 'click-outside-on-mobile'
327
327
  });
328
- useEffect(() => {
329
- if (fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
330
- // This is the old version of the hook, so we skip it when the flag is enabled
331
- return;
332
- }
333
-
334
- // Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
335
- // after SSR hydration. This should only run once, after the initial render on the client.
336
- setSideNavState({
337
- desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
338
- mobile: 'collapsed',
339
- flyout: 'closed',
340
- lastTrigger: null
341
- });
342
- }, [initialDefaultCollapsed, setSideNavState]);
343
328
 
344
- // Moving to `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
329
+ // Using `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
345
330
  // For SSR the new API is still necessary
346
331
  useLayoutEffect(() => {
347
- if (!fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
348
- // This is the new version of the hook, so we skip it when the flag is disabled
349
- return;
350
- }
351
332
  if (sideNavState !== null) {
352
333
  // Only need to do an initial sync if it hasn't been initialized from Root
353
334
  return;
@@ -49,7 +49,7 @@ export const SideNavToggleButton = ({
49
49
 
50
50
  // When default state is provided to `Root` the state in context will already be
51
51
  // initialized in SSR
52
- const [isSideNavExpanded, setIsSideNavExpanded] = useState(sideNavState === null || !fg('platform_dst_nav4_side_nav_default_collapsed_api') ? !defaultCollapsed : isSideNavExpandedOnDesktop);
52
+ const [isSideNavExpanded, setIsSideNavExpanded] = useState(sideNavState === null ? !defaultCollapsed : isSideNavExpandedOnDesktop);
53
53
  const ref = useContext(SideNavToggleButtonAttachRef);
54
54
 
55
55
  /**
@@ -1,5 +1,4 @@
1
1
  import React, { useState } from 'react';
2
- import { fg } from '@atlaskit/platform-feature-flags';
3
2
  import { SetSideNavVisibilityState, SideNavVisibilityState } from './visibility-context';
4
3
 
5
4
  /**
@@ -9,7 +8,7 @@ export const SideNavVisibilityProvider = ({
9
8
  children,
10
9
  defaultCollapsed
11
10
  }) => {
12
- const initialState = typeof defaultCollapsed === 'boolean' && fg('platform_dst_nav4_side_nav_default_collapsed_api') ? {
11
+ const initialState = typeof defaultCollapsed === 'boolean' ? {
13
12
  desktop: defaultCollapsed ? 'collapsed' : 'expanded',
14
13
  mobile: 'collapsed',
15
14
  flyout: 'closed',
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { IconButton } from "@atlaskit/button/new";
3
- import CrossIcon from "@atlaskit/icon/core/cross";
2
+ import { IconButton } from '@atlaskit/button/new';
3
+ import CrossIcon from '@atlaskit/icon/core/cross';
4
4
  /**
5
5
  * __Close button__
6
6
  *
@@ -5,7 +5,7 @@ import React, { useCallback, useContext } from 'react';
5
5
  import Heading from '@atlaskit/heading';
6
6
  import { Flex } from '@atlaskit/primitives/compiled';
7
7
  import { CloseButton } from './close-button';
8
- import { OnCloseContext, SetIsOpenContext, useTitleId } from './flyout-menu-item-context';
8
+ import { OnCloseContext, useTitleId } from './flyout-menu-item-context';
9
9
  var headerStyles = {
10
10
  root: "_zulp12x7 _bozg1b66 _85i512x7 _1e0c1txw _2lx21bp4",
11
11
  flex: "_zulppxbi _1bah1yb4 _2lx21sbv _4cvr1h6o _1bsb1osq _bozgv77o"
@@ -23,12 +23,11 @@ export var FlyoutHeader = function FlyoutHeader(props) {
23
23
  title = props.title,
24
24
  closeButtonLabel = props.closeButtonLabel;
25
25
  var id = useTitleId();
26
- var setIsOpen = useContext(SetIsOpenContext);
27
- var onClose = useContext(OnCloseContext);
28
- var handleClose = useCallback(function () {
29
- onClose === null || onClose === void 0 || onClose();
30
- setIsOpen(false);
31
- }, [setIsOpen, onClose]);
26
+ var onCloseRef = useContext(OnCloseContext);
27
+ var handleClose = useCallback(function (event) {
28
+ var _onCloseRef$current;
29
+ (_onCloseRef$current = onCloseRef.current) === null || _onCloseRef$current === void 0 || _onCloseRef$current.call(onCloseRef, event, 'close-button');
30
+ }, [onCloseRef]);
32
31
  return /*#__PURE__*/React.createElement("div", {
33
32
  "data-testid": testId,
34
33
  className: ax([headerStyles.root])
@@ -40,7 +39,7 @@ export var FlyoutHeader = function FlyoutHeader(props) {
40
39
  testId: testId && "".concat(testId, "--close-button")
41
40
  }), /*#__PURE__*/React.createElement(Heading, {
42
41
  size: "xsmall",
43
- as: "span",
42
+ as: "h2",
44
43
  id: id
45
44
  }, title)), children);
46
45
  };
@@ -3,11 +3,11 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import "./flyout-menu-item-content.compiled.css";
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
5
  import React, { forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
6
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
6
7
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
7
8
  import { fg } from '@atlaskit/platform-feature-flags';
8
9
  import { PopupContent } from '@atlaskit/popup/experimental';
9
- import { OnCloseProvider, SetIsOpenContext, TitleIdContextProvider } from './flyout-menu-item-context';
10
-
10
+ import { OnCloseContext, SetIsOpenContext, TitleIdContextProvider } from './flyout-menu-item-context';
11
11
  /**
12
12
  * The vertical offset in px to ensure the flyout container does not exceed the bounds of
13
13
  * the window. This matches the padding of the content container, and it's position within
@@ -38,10 +38,49 @@ export var FlyoutMenuItemContent = /*#__PURE__*/forwardRef(function (_ref, forwa
38
38
  onClose = _ref.onClose,
39
39
  autoFocus = _ref.autoFocus;
40
40
  var setIsOpen = useContext(SetIsOpenContext);
41
- var handleClose = useCallback(function () {
41
+ var onCloseRef = useContext(OnCloseContext);
42
+ var _useAnalyticsEvents = useAnalyticsEvents(),
43
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
44
+
45
+ // The source of the close is not accessible to the consumer, it is determined within the
46
+ // handleClose function, or passed in as a parameter in FlyoutMenuItemTrigger (outside-click),
47
+ // or FlyoutHeader (close-button).
48
+ var handleClose = useCallback(function (event, source) {
49
+ if (fg("platform_dst_nav4_flyout_menu_slots_close_button")) {
50
+ // Use the passed source if provided, otherwise determine from event
51
+ var determinedSource = source || 'other';
52
+ if (!source) {
53
+ if (event instanceof KeyboardEvent) {
54
+ var keyboardEvent = event;
55
+ if (keyboardEvent.key === 'Escape' || keyboardEvent.key === 'Esc') {
56
+ determinedSource = 'escape-key';
57
+ }
58
+ } else if (event instanceof MouseEvent) {
59
+ if (event && 'type' in event && event.type === 'click') {
60
+ determinedSource = 'outside-click';
61
+ }
62
+ }
63
+ }
64
+
65
+ // When flyout menu is closed, fire analytics event
66
+ var navigationAnalyticsEvent = createAnalyticsEvent({
67
+ source: 'sideNav',
68
+ actionSubject: 'flyoutMenu',
69
+ action: 'closed',
70
+ attributes: {
71
+ closeSource: determinedSource
72
+ }
73
+ });
74
+ navigationAnalyticsEvent.fire('navigation');
75
+ }
42
76
  onClose === null || onClose === void 0 || onClose();
43
77
  setIsOpen(false);
44
- }, [setIsOpen, onClose]);
78
+ }, [setIsOpen, onClose, createAnalyticsEvent]);
79
+
80
+ // Register handleClose in the ref to allow the FlyoutMenuItemTrigger to access it
81
+ useEffect(function () {
82
+ onCloseRef.current = handleClose;
83
+ }, [handleClose, onCloseRef]);
45
84
  var titleId = useId();
46
85
  return /*#__PURE__*/React.createElement(PopupContent, {
47
86
  appearance: "UNSAFE_modal-below-sm",
@@ -80,13 +119,9 @@ export var FlyoutMenuItemContent = /*#__PURE__*/forwardRef(function (_ref, forwa
80
119
  update: update
81
120
  }, fg("platform_dst_nav4_flyout_menu_slots_close_button") ? /*#__PURE__*/React.createElement(TitleIdContextProvider, {
82
121
  value: titleId
83
- }, /*#__PURE__*/React.createElement(OnCloseProvider, {
84
- value: function value() {
85
- return onClose;
86
- }
87
122
  }, /*#__PURE__*/React.createElement("div", {
88
123
  className: ax([flyoutMenuItemContentContainerStyles.container])
89
- }, children))) : children);
124
+ }, children)) : children);
90
125
  });
91
126
  });
92
127
  function createResizeObserver(update) {
@@ -1,6 +1,5 @@
1
1
  import { createContext, useContext } from 'react';
2
2
  import noop from '@atlaskit/ds-lib/noop';
3
-
4
3
  /**
5
4
  * __Is open context__
6
5
  *
@@ -24,16 +23,13 @@ export var useSetFlyoutMenuOpen = function useSetFlyoutMenuOpen() {
24
23
  /**
25
24
  * __On close context__
26
25
  *
27
- * A context for storing the onClose value of the FlyoutMenuItem.
28
- */
29
- export var OnCloseContext = /*#__PURE__*/createContext(null);
30
-
31
- /**
32
- * __On close provider__
33
- *
34
- * A context provider for supplying the onClose function to the FlyoutHeader.
26
+ * A context for storing a ref to the onClose handler with source information.This
27
+ * is used by FlyoutMenuItemContent, FlyoutMenuItemTrigger and FlyoutHeader to store
28
+ * the on close function and source information for closing the flyout menu.
35
29
  */
36
- export var OnCloseProvider = OnCloseContext.Provider;
30
+ export var OnCloseContext = /*#__PURE__*/createContext({
31
+ current: null
32
+ });
37
33
 
38
34
  /**
39
35
  * __Title id context__
@@ -2,11 +2,13 @@
2
2
  import "./flyout-menu-item-trigger.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
- import { forwardRef } from 'react';
5
+ import { forwardRef, useCallback, useContext } from 'react';
6
6
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
7
7
  import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
8
+ import { fg } from '@atlaskit/platform-feature-flags';
8
9
  import { PopupTrigger } from '@atlaskit/popup/experimental';
9
10
  import { MenuItemBase } from '../menu-item';
11
+ import { IsOpenContext, OnCloseContext } from './flyout-menu-item-context';
10
12
  var elemAfterStyles = {
11
13
  root: "_18zr12x7 _1tz3r0mg"
12
14
  };
@@ -27,6 +29,18 @@ export var FlyoutMenuItemTrigger = /*#__PURE__*/forwardRef(function (_ref, forwa
27
29
  isDragging = _ref.isDragging,
28
30
  hasDragIndicator = _ref.hasDragIndicator,
29
31
  dropIndicator = _ref.dropIndicator;
32
+ var isOpen = useContext(IsOpenContext);
33
+ var onCloseRef = useContext(OnCloseContext);
34
+ var handleClick = useCallback(function (event, analyticsEvent) {
35
+ // If the flyout is open and the trigger is clicked, close the flyout and call the onClick
36
+ // handler with the source information set to 'outside-click'.
37
+ if (fg('platform_dst_nav4_flyout_menu_slots_close_button')) {
38
+ if (isOpen && onCloseRef.current) {
39
+ onCloseRef.current(event, 'outside-click');
40
+ }
41
+ }
42
+ onClick === null || onClick === void 0 || onClick(event, analyticsEvent);
43
+ }, [isOpen, onCloseRef, onClick]);
30
44
  return /*#__PURE__*/React.createElement(PopupTrigger, null, function (_ref2) {
31
45
  var ref = _ref2.ref,
32
46
  ariaControls = _ref2['aria-controls'],
@@ -44,7 +58,7 @@ export var FlyoutMenuItemTrigger = /*#__PURE__*/forwardRef(function (_ref, forwa
44
58
  color: "currentColor",
45
59
  size: "small"
46
60
  })),
47
- onClick: onClick,
61
+ onClick: handleClick,
48
62
  ariaControls: ariaControls,
49
63
  ariaExpanded: ariaExpanded,
50
64
  ariaHasPopup: ariaHasPopup,
@@ -1,11 +1,12 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
- import React, { forwardRef, useEffect } from 'react';
2
+ import React, { forwardRef, useEffect, useRef } from 'react';
3
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
3
4
  import useControlled from '@atlaskit/ds-lib/use-controlled';
4
5
  import usePreviousValue from '@atlaskit/ds-lib/use-previous-value';
5
6
  import { fg } from '@atlaskit/platform-feature-flags';
6
7
  import { Popup } from '@atlaskit/popup/experimental';
7
8
  import { MenuListItem } from '../menu-list-item';
8
- import { IsOpenContext, SetIsOpenContext } from './flyout-menu-item-context';
9
+ import { IsOpenContext, OnCloseContext, SetIsOpenContext } from './flyout-menu-item-context';
9
10
  /**
10
11
  * __FlyoutMenuItem__
11
12
  *
@@ -40,6 +41,9 @@ export var FlyoutMenuItem = /*#__PURE__*/forwardRef(function (_ref, forwardedRef
40
41
  isOpen = _useControlled2[0],
41
42
  setIsOpen = _useControlled2[1];
42
43
  var previousIsOpen = usePreviousValue(isOpen);
44
+ var onCloseRef = useRef(null);
45
+ var _useAnalyticsEvents = useAnalyticsEvents(),
46
+ createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
43
47
  useEffect(function () {
44
48
  if (previousIsOpen === undefined || previousIsOpen === isOpen) {
45
49
  /**
@@ -51,17 +55,31 @@ export var FlyoutMenuItem = /*#__PURE__*/forwardRef(function (_ref, forwardedRef
51
55
  */
52
56
  return;
53
57
  }
58
+
59
+ // When flyout menu is opened, fire analytics event
60
+ if (isOpen && previousIsOpen === false) {
61
+ if (fg('platform_dst_nav4_flyout_menu_slots_close_button')) {
62
+ var navigationAnalyticsEvent = createAnalyticsEvent({
63
+ source: 'sideNav',
64
+ actionSubject: 'flyoutMenu',
65
+ action: 'opened'
66
+ });
67
+ navigationAnalyticsEvent.fire('navigation');
68
+ }
69
+ }
54
70
  onOpenChange === null || onOpenChange === void 0 || onOpenChange(isOpen);
55
- }, [isOpen, onOpenChange, previousIsOpen]);
71
+ }, [isOpen, onOpenChange, previousIsOpen, createAnalyticsEvent]);
56
72
  return /*#__PURE__*/React.createElement(IsOpenContext.Provider, {
57
73
  value: isOpen
58
74
  }, /*#__PURE__*/React.createElement(SetIsOpenContext.Provider, {
59
75
  value: setIsOpen
76
+ }, /*#__PURE__*/React.createElement(OnCloseContext.Provider, {
77
+ value: onCloseRef
60
78
  }, /*#__PURE__*/React.createElement(MenuListItem, {
61
79
  ref: forwardedRef
62
80
  }, /*#__PURE__*/React.createElement(Popup, {
63
81
  id: id,
64
82
  isOpen: isOpen,
65
83
  role: fg('platform_dst_nav4_flyout_menu_slots_close_button') ? 'dialog' : undefined
66
- }, children))));
84
+ }, children)))));
67
85
  });
@@ -334,29 +334,10 @@ function SideNavInternal(_ref) {
334
334
  var toggleVisibilityByClickOutsideOnMobile = useToggleSideNav({
335
335
  trigger: 'click-outside-on-mobile'
336
336
  });
337
- useEffect(function () {
338
- if (fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
339
- // This is the old version of the hook, so we skip it when the flag is enabled
340
- return;
341
- }
342
-
343
- // Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
344
- // after SSR hydration. This should only run once, after the initial render on the client.
345
- setSideNavState({
346
- desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
347
- mobile: 'collapsed',
348
- flyout: 'closed',
349
- lastTrigger: null
350
- });
351
- }, [initialDefaultCollapsed, setSideNavState]);
352
337
 
353
- // Moving to `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
338
+ // Using `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
354
339
  // For SSR the new API is still necessary
355
340
  useLayoutEffect(function () {
356
- if (!fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
357
- // This is the new version of the hook, so we skip it when the flag is disabled
358
- return;
359
- }
360
341
  if (sideNavState !== null) {
361
342
  // Only need to do an initial sync if it hasn't been initialized from Root
362
343
  return;
@@ -52,7 +52,7 @@ export var SideNavToggleButton = function SideNavToggleButton(_ref) {
52
52
 
53
53
  // When default state is provided to `Root` the state in context will already be
54
54
  // initialized in SSR
55
- var _useState = useState(sideNavState === null || !fg('platform_dst_nav4_side_nav_default_collapsed_api') ? !defaultCollapsed : isSideNavExpandedOnDesktop),
55
+ var _useState = useState(sideNavState === null ? !defaultCollapsed : isSideNavExpandedOnDesktop),
56
56
  _useState2 = _slicedToArray(_useState, 2),
57
57
  isSideNavExpanded = _useState2[0],
58
58
  setIsSideNavExpanded = _useState2[1];
@@ -1,6 +1,5 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import React, { useState } from 'react';
3
- import { fg } from '@atlaskit/platform-feature-flags';
4
3
  import { SetSideNavVisibilityState, SideNavVisibilityState } from './visibility-context';
5
4
 
6
5
  /**
@@ -9,7 +8,7 @@ import { SetSideNavVisibilityState, SideNavVisibilityState } from './visibility-
9
8
  export var SideNavVisibilityProvider = function SideNavVisibilityProvider(_ref) {
10
9
  var children = _ref.children,
11
10
  defaultCollapsed = _ref.defaultCollapsed;
12
- var initialState = typeof defaultCollapsed === 'boolean' && fg('platform_dst_nav4_side_nav_default_collapsed_api') ? {
11
+ var initialState = typeof defaultCollapsed === 'boolean' ? {
13
12
  desktop: defaultCollapsed ? 'collapsed' : 'expanded',
14
13
  mobile: 'collapsed',
15
14
  flyout: 'closed',
@@ -12,7 +12,7 @@ type CloseButtonProps = {
12
12
  * should trigger the same close logic as the top-level flyout menu
13
13
  * component.
14
14
  */
15
- onClick?: () => void;
15
+ onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
16
16
  /**
17
17
  * A unique string that appears as data attribute data-testid in the
18
18
  * rendered code, serving as a hook for automated tests.
@@ -3,6 +3,7 @@
3
3
  * @jsx jsx
4
4
  */
5
5
  import React from 'react';
6
+ export type FlyoutCloseSource = 'close-button' | 'escape-key' | 'outside-click' | 'other';
6
7
  export type FlyoutMenuItemContentProps = {
7
8
  /**
8
9
  * The contents of the flyout menu.
@@ -1,3 +1,4 @@
1
+ import type { FlyoutCloseSource } from './flyout-menu-item-content';
1
2
  /**
2
3
  * __Is open context__
3
4
  *
@@ -15,15 +16,11 @@ export declare const useSetFlyoutMenuOpen: () => (value: boolean) => void;
15
16
  /**
16
17
  * __On close context__
17
18
  *
18
- * A context for storing the onClose value of the FlyoutMenuItem.
19
+ * A context for storing a ref to the onClose handler with source information.This
20
+ * is used by FlyoutMenuItemContent, FlyoutMenuItemTrigger and FlyoutHeader to store
21
+ * the on close function and source information for closing the flyout menu.
19
22
  */
20
- export declare const OnCloseContext: import("react").Context<(() => void) | null | undefined>;
21
- /**
22
- * __On close provider__
23
- *
24
- * A context provider for supplying the onClose function to the FlyoutHeader.
25
- */
26
- export declare const OnCloseProvider: import("react").Provider<(() => void) | null | undefined>;
23
+ export declare const OnCloseContext: import("react").Context<import("react").MutableRefObject<((event: Event | React.MouseEvent<HTMLButtonElement> | KeyboardEvent | MouseEvent | null, source?: FlyoutCloseSource) => void) | null>>;
27
24
  /**
28
25
  * __Title id context__
29
26
  *
@@ -12,7 +12,7 @@ type CloseButtonProps = {
12
12
  * should trigger the same close logic as the top-level flyout menu
13
13
  * component.
14
14
  */
15
- onClick?: () => void;
15
+ onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
16
16
  /**
17
17
  * A unique string that appears as data attribute data-testid in the
18
18
  * rendered code, serving as a hook for automated tests.
@@ -3,6 +3,7 @@
3
3
  * @jsx jsx
4
4
  */
5
5
  import React from 'react';
6
+ export type FlyoutCloseSource = 'close-button' | 'escape-key' | 'outside-click' | 'other';
6
7
  export type FlyoutMenuItemContentProps = {
7
8
  /**
8
9
  * The contents of the flyout menu.
@@ -1,3 +1,4 @@
1
+ import type { FlyoutCloseSource } from './flyout-menu-item-content';
1
2
  /**
2
3
  * __Is open context__
3
4
  *
@@ -15,15 +16,11 @@ export declare const useSetFlyoutMenuOpen: () => (value: boolean) => void;
15
16
  /**
16
17
  * __On close context__
17
18
  *
18
- * A context for storing the onClose value of the FlyoutMenuItem.
19
+ * A context for storing a ref to the onClose handler with source information.This
20
+ * is used by FlyoutMenuItemContent, FlyoutMenuItemTrigger and FlyoutHeader to store
21
+ * the on close function and source information for closing the flyout menu.
19
22
  */
20
- export declare const OnCloseContext: import("react").Context<(() => void) | null | undefined>;
21
- /**
22
- * __On close provider__
23
- *
24
- * A context provider for supplying the onClose function to the FlyoutHeader.
25
- */
26
- export declare const OnCloseProvider: import("react").Provider<(() => void) | null | undefined>;
23
+ export declare const OnCloseContext: import("react").Context<import("react").MutableRefObject<((event: Event | React.MouseEvent<HTMLButtonElement> | KeyboardEvent | MouseEvent | null, source?: FlyoutCloseSource) => void) | null>>;
27
24
  /**
28
25
  * __Title id context__
29
26
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/navigation-system",
3
- "version": "5.23.0",
3
+ "version": "5.25.0",
4
4
  "description": "The latest navigation system for Atlassian apps.",
5
5
  "repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
6
6
  "author": "Atlassian Pty Ltd",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@atlaskit/analytics-next": "^11.1.0",
70
- "@atlaskit/avatar": "^25.6.0",
70
+ "@atlaskit/avatar": "^25.7.0",
71
71
  "@atlaskit/button": "^23.9.0",
72
72
  "@atlaskit/css": "^0.19.0",
73
73
  "@atlaskit/ds-lib": "^5.3.0",
@@ -103,7 +103,7 @@
103
103
  "@atlaskit/banner": "^14.0.0",
104
104
  "@atlaskit/breadcrumbs": "^15.3.0",
105
105
  "@atlaskit/dropdown-menu": "^16.4.0",
106
- "@atlaskit/form": "^15.2.0",
106
+ "@atlaskit/form": "^15.3.0",
107
107
  "@atlaskit/link": "^3.3.0",
108
108
  "@atlaskit/lozenge": "^13.3.0",
109
109
  "@atlaskit/menu": "^8.4.0",
@@ -117,7 +117,7 @@
117
117
  "@atlaskit/textfield": "^8.2.0",
118
118
  "@atlassian/feature-flags-test-utils": "^1.0.0",
119
119
  "@atlassian/gemini": "^1.28.0",
120
- "@atlassian/search-dialog": "^9.10.0",
120
+ "@atlassian/search-dialog": "^9.11.0",
121
121
  "@atlassian/ssr-tests": "workspace:^",
122
122
  "@atlassian/test-utils": "^1.0.0",
123
123
  "@atlassian/testing-library": "^0.4.0",
@@ -154,9 +154,6 @@
154
154
  "platform_dst_nav4_fhs_instrumentation_1": {
155
155
  "type": "boolean"
156
156
  },
157
- "platform_dst_nav4_side_nav_default_collapsed_api": {
158
- "type": "boolean"
159
- },
160
157
  "platform-component-visual-refresh": {
161
158
  "type": "boolean",
162
159
  "referenceOnly": true