@atlaskit/navigation-system 4.4.0 → 4.5.1

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 (32) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cjs/ui/page-layout/root.js +8 -3
  3. package/dist/cjs/ui/page-layout/side-nav/is-side-nav-shortcut-enabled-context.js +36 -0
  4. package/dist/cjs/ui/page-layout/side-nav/side-nav.js +9 -2
  5. package/dist/cjs/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +59 -0
  6. package/dist/cjs/ui/top-nav-items/icon-renderer.js +44 -0
  7. package/dist/cjs/ui/top-nav-items/themed/button.js +8 -2
  8. package/dist/es2019/ui/page-layout/root.js +7 -3
  9. package/dist/es2019/ui/page-layout/side-nav/is-side-nav-shortcut-enabled-context.js +29 -0
  10. package/dist/es2019/ui/page-layout/side-nav/side-nav.js +9 -2
  11. package/dist/es2019/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +54 -0
  12. package/dist/es2019/ui/top-nav-items/icon-renderer.js +36 -0
  13. package/dist/es2019/ui/top-nav-items/themed/button.js +8 -2
  14. package/dist/esm/ui/page-layout/root.js +8 -3
  15. package/dist/esm/ui/page-layout/side-nav/is-side-nav-shortcut-enabled-context.js +28 -0
  16. package/dist/esm/ui/page-layout/side-nav/side-nav.js +9 -2
  17. package/dist/esm/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +53 -0
  18. package/dist/esm/ui/top-nav-items/icon-renderer.js +37 -0
  19. package/dist/esm/ui/top-nav-items/themed/button.js +8 -2
  20. package/dist/types/ui/page-layout/root.d.ts +19 -1
  21. package/dist/types/ui/page-layout/side-nav/is-side-nav-shortcut-enabled-context.d.ts +13 -0
  22. package/dist/types/ui/page-layout/side-nav/side-nav.d.ts +13 -1
  23. package/dist/types/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.d.ts +6 -0
  24. package/dist/types/ui/top-nav-items/icon-renderer.d.ts +17 -0
  25. package/dist/types/ui/top-nav-items/themed/button.d.ts +1 -1
  26. package/dist/types-ts4.5/ui/page-layout/root.d.ts +19 -1
  27. package/dist/types-ts4.5/ui/page-layout/side-nav/is-side-nav-shortcut-enabled-context.d.ts +13 -0
  28. package/dist/types-ts4.5/ui/page-layout/side-nav/side-nav.d.ts +13 -1
  29. package/dist/types-ts4.5/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.d.ts +6 -0
  30. package/dist/types-ts4.5/ui/top-nav-items/icon-renderer.d.ts +17 -0
  31. package/dist/types-ts4.5/ui/top-nav-items/themed/button.d.ts +1 -1
  32. package/package.json +13 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @atlassian/navigation-system
2
2
 
3
+ ## 4.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`f5e53b7b1e130`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f5e53b7b1e130) -
8
+ Use IconRenderer for Themed IconButtons to mitigate their DOM be recreated during SSR -> SPA
9
+ hydration, which delays TTVC. This is an experiment behind
10
+ platform_themed_button_use_icon_renderer.
11
+
12
+ ## 4.5.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`8a71ce992f8c8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8a71ce992f8c8) -
17
+ `SideNav` now provides a built-in keyboard shortcut for expanding and collapsing. This is behind
18
+ the feature gate `navx-full-height-sidebar`.
19
+ - The shortcut key is `Ctrl` + `[`.
20
+ - The shortcut is not enabled by default.
21
+ - The prop `isSideNavShortcutEnabled` has been added to `Root`, as a way to control whether the
22
+ shortcut is enabled (whether the keyboard event listener is binded). It defaults to `false`.
23
+ - The prop `canToggleWithShortcut()` has been added to `SideNav`, as a way to run additional
24
+ checks after the shortcut is pressed, before the side nav is toggled.
25
+ - The shortcut will also be ignored if there are any open modals. This check is behind the feature
26
+ gate `platform-dst-open-layer-observer-layer-type`.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies
31
+
3
32
  ## 4.4.0
4
33
 
5
34
  ### Minor Changes
@@ -15,6 +15,7 @@ var _skipLinksProvider = require("../../context/skip-links/skip-links-provider")
15
15
  var _topNavStartContextProvider = require("../../context/top-nav-start/top-nav-start-context-provider");
16
16
  var _hoistSlotSizesContext = require("./hoist-slot-sizes-context");
17
17
  var _elementContext = require("./side-nav/element-context");
18
+ var _isSideNavShortcutEnabledContext = require("./side-nav/is-side-nav-shortcut-enabled-context");
18
19
  var _toggleButtonProvider = require("./side-nav/toggle-button-provider");
19
20
  var _visibilityProvider = require("./side-nav/visibility-provider");
20
21
  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); }
@@ -36,7 +37,9 @@ function Root(_ref) {
36
37
  _ref$skipLinksLabel = _ref.skipLinksLabel,
37
38
  skipLinksLabel = _ref$skipLinksLabel === void 0 ? 'Skip to:' : _ref$skipLinksLabel,
38
39
  testId = _ref.testId,
39
- defaultSideNavCollapsed = _ref.defaultSideNavCollapsed;
40
+ defaultSideNavCollapsed = _ref.defaultSideNavCollapsed,
41
+ _ref$isSideNavShortcu = _ref.isSideNavShortcutEnabled,
42
+ isSideNavShortcutEnabled = _ref$isSideNavShortcu === void 0 ? false : _ref$isSideNavShortcu;
40
43
  var ref = (0, _react.useRef)(null);
41
44
  (0, _react.useEffect)(function () {
42
45
  if (process.env.NODE_ENV !== 'production') {
@@ -53,7 +56,9 @@ function Root(_ref) {
53
56
  }, []);
54
57
  return /*#__PURE__*/_react.default.createElement(_visibilityProvider.SideNavVisibilityProvider, {
55
58
  defaultCollapsed: defaultSideNavCollapsed
56
- }, /*#__PURE__*/_react.default.createElement(_toggleButtonProvider.SideNavToggleButtonProvider, null, /*#__PURE__*/_react.default.createElement(_elementContext.SideNavElementProvider, null, /*#__PURE__*/_react.default.createElement(_topNavStartContextProvider.TopNavStartProvider, null, /*#__PURE__*/_react.default.createElement(_openLayerObserver.OpenLayerObserver, null, /*#__PURE__*/_react.default.createElement(_hoistSlotSizesContext.DangerouslyHoistSlotSizes.Provider, {
59
+ }, /*#__PURE__*/_react.default.createElement(_toggleButtonProvider.SideNavToggleButtonProvider, null, /*#__PURE__*/_react.default.createElement(_elementContext.SideNavElementProvider, null, /*#__PURE__*/_react.default.createElement(_isSideNavShortcutEnabledContext.IsSideNavShortcutEnabledProvider, {
60
+ isSideNavShortcutEnabled: isSideNavShortcutEnabled
61
+ }, /*#__PURE__*/_react.default.createElement(_topNavStartContextProvider.TopNavStartProvider, null, /*#__PURE__*/_react.default.createElement(_openLayerObserver.OpenLayerObserver, null, /*#__PURE__*/_react.default.createElement(_hoistSlotSizesContext.DangerouslyHoistSlotSizes.Provider, {
57
62
  value: UNSAFE_dangerouslyHoistSlotSizes
58
63
  }, /*#__PURE__*/_react.default.createElement(_skipLinksProvider.SkipLinksProvider, {
59
64
  label: skipLinksLabel,
@@ -63,5 +68,5 @@ function Root(_ref) {
63
68
  className: (0, _runtime.ax)([styles.root, xcss]),
64
69
  id: gridRootId,
65
70
  "data-testid": testId
66
- }, children))))))));
71
+ }, children)))))))));
67
72
  }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.IsSideNavShortcutEnabledProvider = IsSideNavShortcutEnabledProvider;
8
+ exports.useIsSideNavShortcutEnabled = useIsSideNavShortcutEnabled;
9
+ var _react = _interopRequireWildcard(require("react"));
10
+ 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); }
11
+ /**
12
+ * Context for whether the side nav toggle shortcut is enabled.
13
+ *
14
+ * Used to share the `isSideNavShortcutEnabled` prop value from `Root` with other components,
15
+ * so the visual keyboard shortcut in tooltips can be conditionally displayed.
16
+ */
17
+ var IsSideNavShortcutEnabledContext = /*#__PURE__*/(0, _react.createContext)(false);
18
+
19
+ /**
20
+ * Provider for the `IsSideNavShortcutEnabledContext`.
21
+ */
22
+ function IsSideNavShortcutEnabledProvider(_ref) {
23
+ var children = _ref.children,
24
+ isSideNavShortcutEnabled = _ref.isSideNavShortcutEnabled;
25
+ return /*#__PURE__*/_react.default.createElement(IsSideNavShortcutEnabledContext.Provider, {
26
+ value: isSideNavShortcutEnabled
27
+ }, children);
28
+ }
29
+
30
+ /**
31
+ * Returns the value of the `isSideNavShortcutEnabled` prop from the `Root` component, which
32
+ * is shared through context.
33
+ */
34
+ function useIsSideNavShortcutEnabled() {
35
+ return (0, _react.useContext)(IsSideNavShortcutEnabledContext);
36
+ }
@@ -36,6 +36,7 @@ var _elementContext = require("./element-context");
36
36
  var _flyoutCloseDelayMs = require("./flyout-close-delay-ms");
37
37
  var _toggleButtonContext = require("./toggle-button-context");
38
38
  var _useExpandSideNav = require("./use-expand-side-nav");
39
+ var _useSideNavToggleKeyboardShortcut = require("./use-side-nav-toggle-keyboard-shortcut");
39
40
  var _useSideNavVisibility2 = require("./use-side-nav-visibility");
40
41
  var _useSideNavVisibilityCallbacks = require("./use-side-nav-visibility-callbacks");
41
42
  var _useToggleSideNav = require("./use-toggle-side-nav");
@@ -100,7 +101,8 @@ function SideNavInternal(_ref) {
100
101
  onCollapse = _ref.onCollapse,
101
102
  onPeekStart = _ref.onPeekStart,
102
103
  onPeekEnd = _ref.onPeekEnd,
103
- providedId = _ref.id;
104
+ providedId = _ref.id,
105
+ canToggleWithShortcut = _ref.canToggleWithShortcut;
104
106
  var id = (0, _idUtils.useLayoutId)({
105
107
  providedId: providedId
106
108
  });
@@ -645,6 +647,9 @@ function SideNavInternal(_ref) {
645
647
  }
646
648
  devTimeOnlyAttributes['data-visible'] = visible.length ? visible.join(',') : 'false';
647
649
  }
650
+ (0, _useSideNavToggleKeyboardShortcut.useSideNavToggleKeyboardShortcut)({
651
+ canToggleWithShortcut: canToggleWithShortcut
652
+ });
648
653
  (0, _useResizingWidthCssVarOnRootElement.useResizingWidthCssVarOnRootElement)({
649
654
  isEnabled: true,
650
655
  cssVar: panelSplitterResizingVar,
@@ -746,6 +751,7 @@ function SideNav(_ref8) {
746
751
  onCollapse = _ref8.onCollapse,
747
752
  onPeekStart = _ref8.onPeekStart,
748
753
  onPeekEnd = _ref8.onPeekEnd,
754
+ canToggleWithShortcut = _ref8.canToggleWithShortcut,
749
755
  id = _ref8.id;
750
756
  return /*#__PURE__*/React.createElement(_openLayerObserver.OpenLayerObserverNamespaceProvider, {
751
757
  namespace: openLayerObserverSideNavNamespace
@@ -759,6 +765,7 @@ function SideNav(_ref8) {
759
765
  onCollapse: onCollapse,
760
766
  onPeekStart: onPeekStart,
761
767
  onPeekEnd: onPeekEnd,
762
- id: id
768
+ id: id,
769
+ canToggleWithShortcut: canToggleWithShortcut
763
770
  }, children));
764
771
  }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.useSideNavToggleKeyboardShortcut = useSideNavToggleKeyboardShortcut;
8
+ var _react = require("react");
9
+ var _bindEventListener = require("bind-event-listener");
10
+ var _useStableRef = _interopRequireDefault(require("@atlaskit/ds-lib/use-stable-ref"));
11
+ var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
12
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
13
+ var _isSideNavShortcutEnabledContext = require("./is-side-nav-shortcut-enabled-context");
14
+ var _useToggleSideNav = require("./use-toggle-side-nav");
15
+ /**
16
+ * Binds the keyboard shortcut to toggle the side nav.
17
+ */
18
+ function useSideNavToggleKeyboardShortcut(_ref) {
19
+ var canToggleWithShortcut = _ref.canToggleWithShortcut;
20
+ var openLayerObserver = (0, _openLayerObserver.useOpenLayerObserver)();
21
+ var toggleVisibilityByShortcut = (0, _useToggleSideNav.useToggleSideNav)({
22
+ trigger: 'keyboard'
23
+ });
24
+ var canToggleWithShortcutStableRef = (0, _useStableRef.default)(canToggleWithShortcut);
25
+ var isSideNavShortcutEnabled = (0, _isSideNavShortcutEnabledContext.useIsSideNavShortcutEnabled)();
26
+ (0, _react.useEffect)(function () {
27
+ if (!(0, _platformFeatureFlags.fg)('navx-full-height-sidebar')) {
28
+ return;
29
+ }
30
+ if (!isSideNavShortcutEnabled) {
31
+ return;
32
+ }
33
+ return (0, _bindEventListener.bind)(window, {
34
+ type: 'keydown',
35
+ listener: function listener(event) {
36
+ if (event.ctrlKey && event.key === '[') {
37
+ if (canToggleWithShortcutStableRef.current && !canToggleWithShortcutStableRef.current()) {
38
+ // Return early if the callback returns false.
39
+ // If the callback is not provided, we assume the shortcut is enabled.
40
+ return;
41
+ }
42
+ if (event.repeat) {
43
+ // Ignore repeated keydown events from holding down the keys
44
+ return;
45
+ }
46
+ if (openLayerObserver.getCount({
47
+ type: 'modal'
48
+ }) > 0 && (0, _platformFeatureFlags.fg)('platform-dst-open-layer-observer-layer-type')) {
49
+ // Return early if there are any open modals
50
+ // This check is behind the layer type FG, as `getCount` will return the count of all layers when
51
+ // the FG is disabled - meaning we would ignore the shortcut if there is any open layer (not just modals).
52
+ return;
53
+ }
54
+ toggleVisibilityByShortcut();
55
+ }
56
+ }
57
+ });
58
+ }, [canToggleWithShortcutStableRef, openLayerObserver, toggleVisibilityByShortcut, isSideNavShortcutEnabled]);
59
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
+ function isIconRenderProp(func) {
11
+ return !func.displayName &&
12
+ // most function components and class components have a displayName, negate them
13
+ !func.render &&
14
+ // forwardRef doesn't require a display name, however it does include a render function, negate them
15
+ typeof func === 'function' // at the very least we need to be a function
16
+ ;
17
+ }
18
+
19
+ /**
20
+ * __Icon renderer__
21
+ *
22
+ * Used to support render props with icons.
23
+ *
24
+ * This is copied from @atlaskit/button to render IconButtons. Some IconButton's DOM element
25
+ * will be re-created during SSR -> SPA hydration without using this, which delays TTVC. As
26
+ * IconRenderer is already used in @atlaskit/button to mitigate this issue, add it here to
27
+ * mitigate the same issue for IconButtons in navigation-system, especially for ThemedIconButton.
28
+ *
29
+ */
30
+ var IconRenderer = function IconRenderer(_ref) {
31
+ var Icon = _ref.icon;
32
+ var isRenderProp = isIconRenderProp(Icon);
33
+ var iconProps = {
34
+ label: '',
35
+ color: 'currentColor',
36
+ size: (0, _platformFeatureFlags.fg)('platform_dst_button_chevron_sizing') ? function (iconName) {
37
+ return iconName.startsWith('Chevron') ? 'small' : 'medium';
38
+ } : undefined
39
+ };
40
+
41
+ // @ts-ignore - TS2322 TypeScript 5.9.2 upgrade
42
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, isRenderProp ? Icon(iconProps) : /*#__PURE__*/_react.default.createElement(Icon, iconProps));
43
+ };
44
+ var _default = exports.default = IconRenderer;
@@ -15,9 +15,11 @@ var _react = _interopRequireWildcard(require("react"));
15
15
  var _react2 = require("@compiled/react");
16
16
  var _forwardRefWithGeneric = _interopRequireDefault(require("@atlaskit/ds-lib/forward-ref-with-generic"));
17
17
  var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
18
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
19
  var _compiled = require("@atlaskit/primitives/compiled");
19
20
  var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
20
21
  var _visuallyHidden = _interopRequireDefault(require("@atlaskit/visually-hidden"));
22
+ var _iconRenderer = _interopRequireDefault(require("../icon-renderer"));
21
23
  var _excluded = ["style", "xcss", "backgroundColor", "padding", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd"],
22
24
  _excluded2 = ["appearance", "shape", "isSelected", "isDisabled"],
23
25
  _excluded3 = ["appearance", "shape", "isSelected", "isDisabled"],
@@ -185,7 +187,9 @@ var ThemedButton = exports.ThemedButton = /*#__PURE__*/(0, _react.forwardRef)(fu
185
187
  ref: ref
186
188
  }), IconBefore && /*#__PURE__*/_react.default.createElement("span", {
187
189
  className: (0, _runtime.ax)([textButtonStyles.iconBefore])
188
- }, /*#__PURE__*/_react.default.createElement(IconBefore, {
190
+ }, (0, _platformFeatureFlags.fg)('platform_themed_button_use_icon_renderer') ? /*#__PURE__*/_react.default.createElement(_iconRenderer.default, {
191
+ icon: IconBefore
192
+ }) : /*#__PURE__*/_react.default.createElement(IconBefore, {
189
193
  label: "",
190
194
  color: "currentColor"
191
195
  })), /*#__PURE__*/_react.default.createElement("span", {
@@ -278,7 +282,9 @@ var ThemedIconButton = exports.ThemedIconButton = /*#__PURE__*/(0, _react.forwar
278
282
  (_triggerProps$onBlur = triggerProps.onBlur) === null || _triggerProps$onBlur === void 0 || _triggerProps$onBlur.call(triggerProps, e);
279
283
  (_props$onBlur = props.onBlur) === null || _props$onBlur === void 0 || _props$onBlur.call(props, e);
280
284
  }
281
- }), /*#__PURE__*/_react.default.createElement(Icon, {
285
+ }), (0, _platformFeatureFlags.fg)('platform_themed_button_use_icon_renderer') ? /*#__PURE__*/_react.default.createElement(_iconRenderer.default, {
286
+ icon: Icon
287
+ }) : /*#__PURE__*/_react.default.createElement(Icon, {
282
288
  label: "",
283
289
  color: "currentColor"
284
290
  }), /*#__PURE__*/_react.default.createElement(_visuallyHidden.default, null, label))
@@ -7,6 +7,7 @@ import { SkipLinksProvider } from '../../context/skip-links/skip-links-provider'
7
7
  import { TopNavStartProvider } from '../../context/top-nav-start/top-nav-start-context-provider';
8
8
  import { DangerouslyHoistSlotSizes } from './hoist-slot-sizes-context';
9
9
  import { SideNavElementProvider } from './side-nav/element-context';
10
+ import { IsSideNavShortcutEnabledProvider } from './side-nav/is-side-nav-shortcut-enabled-context';
10
11
  import { SideNavToggleButtonProvider } from './side-nav/toggle-button-provider';
11
12
  import { SideNavVisibilityProvider } from './side-nav/visibility-provider';
12
13
 
@@ -26,7 +27,8 @@ export function Root({
26
27
  UNSAFE_dangerouslyHoistSlotSizes = false,
27
28
  skipLinksLabel = 'Skip to:',
28
29
  testId,
29
- defaultSideNavCollapsed
30
+ defaultSideNavCollapsed,
31
+ isSideNavShortcutEnabled = false
30
32
  }) {
31
33
  const ref = useRef(null);
32
34
  useEffect(() => {
@@ -53,7 +55,9 @@ This message will not be displayed in production.
53
55
  }, []);
54
56
  return /*#__PURE__*/React.createElement(SideNavVisibilityProvider, {
55
57
  defaultCollapsed: defaultSideNavCollapsed
56
- }, /*#__PURE__*/React.createElement(SideNavToggleButtonProvider, null, /*#__PURE__*/React.createElement(SideNavElementProvider, null, /*#__PURE__*/React.createElement(TopNavStartProvider, null, /*#__PURE__*/React.createElement(OpenLayerObserver, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
58
+ }, /*#__PURE__*/React.createElement(SideNavToggleButtonProvider, null, /*#__PURE__*/React.createElement(SideNavElementProvider, null, /*#__PURE__*/React.createElement(IsSideNavShortcutEnabledProvider, {
59
+ isSideNavShortcutEnabled: isSideNavShortcutEnabled
60
+ }, /*#__PURE__*/React.createElement(TopNavStartProvider, null, /*#__PURE__*/React.createElement(OpenLayerObserver, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
57
61
  value: UNSAFE_dangerouslyHoistSlotSizes
58
62
  }, /*#__PURE__*/React.createElement(SkipLinksProvider, {
59
63
  label: skipLinksLabel,
@@ -63,5 +67,5 @@ This message will not be displayed in production.
63
67
  className: ax([styles.root, xcss]),
64
68
  id: gridRootId,
65
69
  "data-testid": testId
66
- }, children))))))));
70
+ }, children)))))))));
67
71
  }
@@ -0,0 +1,29 @@
1
+ import React, { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * Context for whether the side nav toggle shortcut is enabled.
5
+ *
6
+ * Used to share the `isSideNavShortcutEnabled` prop value from `Root` with other components,
7
+ * so the visual keyboard shortcut in tooltips can be conditionally displayed.
8
+ */
9
+ const IsSideNavShortcutEnabledContext = /*#__PURE__*/createContext(false);
10
+
11
+ /**
12
+ * Provider for the `IsSideNavShortcutEnabledContext`.
13
+ */
14
+ export function IsSideNavShortcutEnabledProvider({
15
+ children,
16
+ isSideNavShortcutEnabled
17
+ }) {
18
+ return /*#__PURE__*/React.createElement(IsSideNavShortcutEnabledContext.Provider, {
19
+ value: isSideNavShortcutEnabled
20
+ }, children);
21
+ }
22
+
23
+ /**
24
+ * Returns the value of the `isSideNavShortcutEnabled` prop from the `Root` component, which
25
+ * is shared through context.
26
+ */
27
+ export function useIsSideNavShortcutEnabled() {
28
+ return useContext(IsSideNavShortcutEnabledContext);
29
+ }
@@ -25,6 +25,7 @@ import { useSideNavRef } from './element-context';
25
25
  import { sideNavFlyoutCloseDelayMs } from './flyout-close-delay-ms';
26
26
  import { SideNavToggleButtonElement } from './toggle-button-context';
27
27
  import { useExpandSideNav } from './use-expand-side-nav';
28
+ import { useSideNavToggleKeyboardShortcut } from './use-side-nav-toggle-keyboard-shortcut';
28
29
  import { useSideNavVisibility } from './use-side-nav-visibility';
29
30
  import { useSideNavVisibilityCallbacks } from './use-side-nav-visibility-callbacks';
30
31
  import { useToggleSideNav } from './use-toggle-side-nav';
@@ -82,7 +83,8 @@ function SideNavInternal({
82
83
  onCollapse,
83
84
  onPeekStart,
84
85
  onPeekEnd,
85
- id: providedId
86
+ id: providedId,
87
+ canToggleWithShortcut
86
88
  }) {
87
89
  var _sideNavState$lastTri;
88
90
  const id = useLayoutId({
@@ -633,6 +635,9 @@ function SideNavInternal({
633
635
  }
634
636
  devTimeOnlyAttributes['data-visible'] = visible.length ? visible.join(',') : 'false';
635
637
  }
638
+ useSideNavToggleKeyboardShortcut({
639
+ canToggleWithShortcut
640
+ });
636
641
  useResizingWidthCssVarOnRootElement({
637
642
  isEnabled: true,
638
643
  cssVar: panelSplitterResizingVar,
@@ -737,6 +742,7 @@ export function SideNav({
737
742
  onCollapse,
738
743
  onPeekStart,
739
744
  onPeekEnd,
745
+ canToggleWithShortcut,
740
746
  id
741
747
  }) {
742
748
  return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
@@ -751,6 +757,7 @@ export function SideNav({
751
757
  onCollapse: onCollapse,
752
758
  onPeekStart: onPeekStart,
753
759
  onPeekEnd: onPeekEnd,
754
- id: id
760
+ id: id,
761
+ canToggleWithShortcut: canToggleWithShortcut
755
762
  }, children));
756
763
  }
@@ -0,0 +1,54 @@
1
+ import { useEffect } from 'react';
2
+ import { bind } from 'bind-event-listener';
3
+ import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
4
+ import { useOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
6
+ import { useIsSideNavShortcutEnabled } from './is-side-nav-shortcut-enabled-context';
7
+ import { useToggleSideNav } from './use-toggle-side-nav';
8
+
9
+ /**
10
+ * Binds the keyboard shortcut to toggle the side nav.
11
+ */
12
+ export function useSideNavToggleKeyboardShortcut({
13
+ canToggleWithShortcut
14
+ }) {
15
+ const openLayerObserver = useOpenLayerObserver();
16
+ const toggleVisibilityByShortcut = useToggleSideNav({
17
+ trigger: 'keyboard'
18
+ });
19
+ const canToggleWithShortcutStableRef = useStableRef(canToggleWithShortcut);
20
+ const isSideNavShortcutEnabled = useIsSideNavShortcutEnabled();
21
+ useEffect(() => {
22
+ if (!fg('navx-full-height-sidebar')) {
23
+ return;
24
+ }
25
+ if (!isSideNavShortcutEnabled) {
26
+ return;
27
+ }
28
+ return bind(window, {
29
+ type: 'keydown',
30
+ listener(event) {
31
+ if (event.ctrlKey && event.key === '[') {
32
+ if (canToggleWithShortcutStableRef.current && !canToggleWithShortcutStableRef.current()) {
33
+ // Return early if the callback returns false.
34
+ // If the callback is not provided, we assume the shortcut is enabled.
35
+ return;
36
+ }
37
+ if (event.repeat) {
38
+ // Ignore repeated keydown events from holding down the keys
39
+ return;
40
+ }
41
+ if (openLayerObserver.getCount({
42
+ type: 'modal'
43
+ }) > 0 && fg('platform-dst-open-layer-observer-layer-type')) {
44
+ // Return early if there are any open modals
45
+ // This check is behind the layer type FG, as `getCount` will return the count of all layers when
46
+ // the FG is disabled - meaning we would ignore the shortcut if there is any open layer (not just modals).
47
+ return;
48
+ }
49
+ toggleVisibilityByShortcut();
50
+ }
51
+ }
52
+ });
53
+ }, [canToggleWithShortcutStableRef, openLayerObserver, toggleVisibilityByShortcut, isSideNavShortcutEnabled]);
54
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
3
+ function isIconRenderProp(func) {
4
+ return !func.displayName &&
5
+ // most function components and class components have a displayName, negate them
6
+ !func.render &&
7
+ // forwardRef doesn't require a display name, however it does include a render function, negate them
8
+ typeof func === 'function' // at the very least we need to be a function
9
+ ;
10
+ }
11
+
12
+ /**
13
+ * __Icon renderer__
14
+ *
15
+ * Used to support render props with icons.
16
+ *
17
+ * This is copied from @atlaskit/button to render IconButtons. Some IconButton's DOM element
18
+ * will be re-created during SSR -> SPA hydration without using this, which delays TTVC. As
19
+ * IconRenderer is already used in @atlaskit/button to mitigate this issue, add it here to
20
+ * mitigate the same issue for IconButtons in navigation-system, especially for ThemedIconButton.
21
+ *
22
+ */
23
+ const IconRenderer = ({
24
+ icon: Icon
25
+ }) => {
26
+ const isRenderProp = isIconRenderProp(Icon);
27
+ let iconProps = {
28
+ label: '',
29
+ color: 'currentColor',
30
+ size: fg('platform_dst_button_chevron_sizing') ? iconName => iconName.startsWith('Chevron') ? 'small' : 'medium' : undefined
31
+ };
32
+
33
+ // @ts-ignore - TS2322 TypeScript 5.9.2 upgrade
34
+ return /*#__PURE__*/React.createElement(React.Fragment, null, isRenderProp ? Icon(iconProps) : /*#__PURE__*/React.createElement(Icon, iconProps));
35
+ };
36
+ export default IconRenderer;
@@ -6,9 +6,11 @@ import React, { forwardRef } from 'react';
6
6
  import { cx } from '@compiled/react';
7
7
  import forwardRefWithGeneric from '@atlaskit/ds-lib/forward-ref-with-generic';
8
8
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
9
+ import { fg } from '@atlaskit/platform-feature-flags';
9
10
  import { Anchor, Pressable } from '@atlaskit/primitives/compiled';
10
11
  import Tooltip from '@atlaskit/tooltip';
11
12
  import VisuallyHidden from '@atlaskit/visually-hidden';
13
+ import IconRenderer from '../icon-renderer';
12
14
 
13
15
  /**
14
16
  * Props from primitives that we explicitly ignore and remove from spread props,
@@ -169,7 +171,9 @@ export const ThemedButton = /*#__PURE__*/forwardRef(function ThemedButton({
169
171
  ref: ref
170
172
  }), IconBefore && /*#__PURE__*/React.createElement("span", {
171
173
  className: ax([textButtonStyles.iconBefore])
172
- }, /*#__PURE__*/React.createElement(IconBefore, {
174
+ }, fg('platform_themed_button_use_icon_renderer') ? /*#__PURE__*/React.createElement(IconRenderer, {
175
+ icon: IconBefore
176
+ }) : /*#__PURE__*/React.createElement(IconBefore, {
173
177
  label: "",
174
178
  color: "currentColor"
175
179
  })), /*#__PURE__*/React.createElement("span", {
@@ -263,7 +267,9 @@ export const ThemedIconButton = /*#__PURE__*/forwardRef(function ThemedIconButto
263
267
  (_triggerProps$onBlur = triggerProps.onBlur) === null || _triggerProps$onBlur === void 0 ? void 0 : _triggerProps$onBlur.call(triggerProps, e);
264
268
  (_props$onBlur = props.onBlur) === null || _props$onBlur === void 0 ? void 0 : _props$onBlur.call(props, e);
265
269
  }
266
- }), /*#__PURE__*/React.createElement(Icon, {
270
+ }), fg('platform_themed_button_use_icon_renderer') ? /*#__PURE__*/React.createElement(IconRenderer, {
271
+ icon: Icon
272
+ }) : /*#__PURE__*/React.createElement(Icon, {
267
273
  label: "",
268
274
  color: "currentColor"
269
275
  }), /*#__PURE__*/React.createElement(VisuallyHidden, null, label)));
@@ -7,6 +7,7 @@ import { SkipLinksProvider } from '../../context/skip-links/skip-links-provider'
7
7
  import { TopNavStartProvider } from '../../context/top-nav-start/top-nav-start-context-provider';
8
8
  import { DangerouslyHoistSlotSizes } from './hoist-slot-sizes-context';
9
9
  import { SideNavElementProvider } from './side-nav/element-context';
10
+ import { IsSideNavShortcutEnabledProvider } from './side-nav/is-side-nav-shortcut-enabled-context';
10
11
  import { SideNavToggleButtonProvider } from './side-nav/toggle-button-provider';
11
12
  import { SideNavVisibilityProvider } from './side-nav/visibility-provider';
12
13
 
@@ -28,7 +29,9 @@ export function Root(_ref) {
28
29
  _ref$skipLinksLabel = _ref.skipLinksLabel,
29
30
  skipLinksLabel = _ref$skipLinksLabel === void 0 ? 'Skip to:' : _ref$skipLinksLabel,
30
31
  testId = _ref.testId,
31
- defaultSideNavCollapsed = _ref.defaultSideNavCollapsed;
32
+ defaultSideNavCollapsed = _ref.defaultSideNavCollapsed,
33
+ _ref$isSideNavShortcu = _ref.isSideNavShortcutEnabled,
34
+ isSideNavShortcutEnabled = _ref$isSideNavShortcu === void 0 ? false : _ref$isSideNavShortcu;
32
35
  var ref = useRef(null);
33
36
  useEffect(function () {
34
37
  if (process.env.NODE_ENV !== 'production') {
@@ -45,7 +48,9 @@ export function Root(_ref) {
45
48
  }, []);
46
49
  return /*#__PURE__*/React.createElement(SideNavVisibilityProvider, {
47
50
  defaultCollapsed: defaultSideNavCollapsed
48
- }, /*#__PURE__*/React.createElement(SideNavToggleButtonProvider, null, /*#__PURE__*/React.createElement(SideNavElementProvider, null, /*#__PURE__*/React.createElement(TopNavStartProvider, null, /*#__PURE__*/React.createElement(OpenLayerObserver, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
51
+ }, /*#__PURE__*/React.createElement(SideNavToggleButtonProvider, null, /*#__PURE__*/React.createElement(SideNavElementProvider, null, /*#__PURE__*/React.createElement(IsSideNavShortcutEnabledProvider, {
52
+ isSideNavShortcutEnabled: isSideNavShortcutEnabled
53
+ }, /*#__PURE__*/React.createElement(TopNavStartProvider, null, /*#__PURE__*/React.createElement(OpenLayerObserver, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
49
54
  value: UNSAFE_dangerouslyHoistSlotSizes
50
55
  }, /*#__PURE__*/React.createElement(SkipLinksProvider, {
51
56
  label: skipLinksLabel,
@@ -55,5 +60,5 @@ export function Root(_ref) {
55
60
  className: ax([styles.root, xcss]),
56
61
  id: gridRootId,
57
62
  "data-testid": testId
58
- }, children))))))));
63
+ }, children)))))))));
59
64
  }
@@ -0,0 +1,28 @@
1
+ import React, { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * Context for whether the side nav toggle shortcut is enabled.
5
+ *
6
+ * Used to share the `isSideNavShortcutEnabled` prop value from `Root` with other components,
7
+ * so the visual keyboard shortcut in tooltips can be conditionally displayed.
8
+ */
9
+ var IsSideNavShortcutEnabledContext = /*#__PURE__*/createContext(false);
10
+
11
+ /**
12
+ * Provider for the `IsSideNavShortcutEnabledContext`.
13
+ */
14
+ export function IsSideNavShortcutEnabledProvider(_ref) {
15
+ var children = _ref.children,
16
+ isSideNavShortcutEnabled = _ref.isSideNavShortcutEnabled;
17
+ return /*#__PURE__*/React.createElement(IsSideNavShortcutEnabledContext.Provider, {
18
+ value: isSideNavShortcutEnabled
19
+ }, children);
20
+ }
21
+
22
+ /**
23
+ * Returns the value of the `isSideNavShortcutEnabled` prop from the `Root` component, which
24
+ * is shared through context.
25
+ */
26
+ export function useIsSideNavShortcutEnabled() {
27
+ return useContext(IsSideNavShortcutEnabledContext);
28
+ }
@@ -29,6 +29,7 @@ import { useSideNavRef } from './element-context';
29
29
  import { sideNavFlyoutCloseDelayMs } from './flyout-close-delay-ms';
30
30
  import { SideNavToggleButtonElement } from './toggle-button-context';
31
31
  import { useExpandSideNav } from './use-expand-side-nav';
32
+ import { useSideNavToggleKeyboardShortcut } from './use-side-nav-toggle-keyboard-shortcut';
32
33
  import { useSideNavVisibility } from './use-side-nav-visibility';
33
34
  import { useSideNavVisibilityCallbacks } from './use-side-nav-visibility-callbacks';
34
35
  import { useToggleSideNav } from './use-toggle-side-nav';
@@ -90,7 +91,8 @@ function SideNavInternal(_ref) {
90
91
  onCollapse = _ref.onCollapse,
91
92
  onPeekStart = _ref.onPeekStart,
92
93
  onPeekEnd = _ref.onPeekEnd,
93
- providedId = _ref.id;
94
+ providedId = _ref.id,
95
+ canToggleWithShortcut = _ref.canToggleWithShortcut;
94
96
  var id = useLayoutId({
95
97
  providedId: providedId
96
98
  });
@@ -635,6 +637,9 @@ function SideNavInternal(_ref) {
635
637
  }
636
638
  devTimeOnlyAttributes['data-visible'] = visible.length ? visible.join(',') : 'false';
637
639
  }
640
+ useSideNavToggleKeyboardShortcut({
641
+ canToggleWithShortcut: canToggleWithShortcut
642
+ });
638
643
  useResizingWidthCssVarOnRootElement({
639
644
  isEnabled: true,
640
645
  cssVar: panelSplitterResizingVar,
@@ -736,6 +741,7 @@ export function SideNav(_ref8) {
736
741
  onCollapse = _ref8.onCollapse,
737
742
  onPeekStart = _ref8.onPeekStart,
738
743
  onPeekEnd = _ref8.onPeekEnd,
744
+ canToggleWithShortcut = _ref8.canToggleWithShortcut,
739
745
  id = _ref8.id;
740
746
  return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
741
747
  namespace: openLayerObserverSideNavNamespace
@@ -749,6 +755,7 @@ export function SideNav(_ref8) {
749
755
  onCollapse: onCollapse,
750
756
  onPeekStart: onPeekStart,
751
757
  onPeekEnd: onPeekEnd,
752
- id: id
758
+ id: id,
759
+ canToggleWithShortcut: canToggleWithShortcut
753
760
  }, children));
754
761
  }
@@ -0,0 +1,53 @@
1
+ import { useEffect } from 'react';
2
+ import { bind } from 'bind-event-listener';
3
+ import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
4
+ import { useOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
6
+ import { useIsSideNavShortcutEnabled } from './is-side-nav-shortcut-enabled-context';
7
+ import { useToggleSideNav } from './use-toggle-side-nav';
8
+
9
+ /**
10
+ * Binds the keyboard shortcut to toggle the side nav.
11
+ */
12
+ export function useSideNavToggleKeyboardShortcut(_ref) {
13
+ var canToggleWithShortcut = _ref.canToggleWithShortcut;
14
+ var openLayerObserver = useOpenLayerObserver();
15
+ var toggleVisibilityByShortcut = useToggleSideNav({
16
+ trigger: 'keyboard'
17
+ });
18
+ var canToggleWithShortcutStableRef = useStableRef(canToggleWithShortcut);
19
+ var isSideNavShortcutEnabled = useIsSideNavShortcutEnabled();
20
+ useEffect(function () {
21
+ if (!fg('navx-full-height-sidebar')) {
22
+ return;
23
+ }
24
+ if (!isSideNavShortcutEnabled) {
25
+ return;
26
+ }
27
+ return bind(window, {
28
+ type: 'keydown',
29
+ listener: function listener(event) {
30
+ if (event.ctrlKey && event.key === '[') {
31
+ if (canToggleWithShortcutStableRef.current && !canToggleWithShortcutStableRef.current()) {
32
+ // Return early if the callback returns false.
33
+ // If the callback is not provided, we assume the shortcut is enabled.
34
+ return;
35
+ }
36
+ if (event.repeat) {
37
+ // Ignore repeated keydown events from holding down the keys
38
+ return;
39
+ }
40
+ if (openLayerObserver.getCount({
41
+ type: 'modal'
42
+ }) > 0 && fg('platform-dst-open-layer-observer-layer-type')) {
43
+ // Return early if there are any open modals
44
+ // This check is behind the layer type FG, as `getCount` will return the count of all layers when
45
+ // the FG is disabled - meaning we would ignore the shortcut if there is any open layer (not just modals).
46
+ return;
47
+ }
48
+ toggleVisibilityByShortcut();
49
+ }
50
+ }
51
+ });
52
+ }, [canToggleWithShortcutStableRef, openLayerObserver, toggleVisibilityByShortcut, isSideNavShortcutEnabled]);
53
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
3
+ function isIconRenderProp(func) {
4
+ return !func.displayName &&
5
+ // most function components and class components have a displayName, negate them
6
+ !func.render &&
7
+ // forwardRef doesn't require a display name, however it does include a render function, negate them
8
+ typeof func === 'function' // at the very least we need to be a function
9
+ ;
10
+ }
11
+
12
+ /**
13
+ * __Icon renderer__
14
+ *
15
+ * Used to support render props with icons.
16
+ *
17
+ * This is copied from @atlaskit/button to render IconButtons. Some IconButton's DOM element
18
+ * will be re-created during SSR -> SPA hydration without using this, which delays TTVC. As
19
+ * IconRenderer is already used in @atlaskit/button to mitigate this issue, add it here to
20
+ * mitigate the same issue for IconButtons in navigation-system, especially for ThemedIconButton.
21
+ *
22
+ */
23
+ var IconRenderer = function IconRenderer(_ref) {
24
+ var Icon = _ref.icon;
25
+ var isRenderProp = isIconRenderProp(Icon);
26
+ var iconProps = {
27
+ label: '',
28
+ color: 'currentColor',
29
+ size: fg('platform_dst_button_chevron_sizing') ? function (iconName) {
30
+ return iconName.startsWith('Chevron') ? 'small' : 'medium';
31
+ } : undefined
32
+ };
33
+
34
+ // @ts-ignore - TS2322 TypeScript 5.9.2 upgrade
35
+ return /*#__PURE__*/React.createElement(React.Fragment, null, isRenderProp ? Icon(iconProps) : /*#__PURE__*/React.createElement(Icon, iconProps));
36
+ };
37
+ export default IconRenderer;
@@ -14,9 +14,11 @@ import React, { forwardRef } from 'react';
14
14
  import { cx } from '@compiled/react';
15
15
  import forwardRefWithGeneric from '@atlaskit/ds-lib/forward-ref-with-generic';
16
16
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
17
+ import { fg } from '@atlaskit/platform-feature-flags';
17
18
  import { Anchor, Pressable } from '@atlaskit/primitives/compiled';
18
19
  import Tooltip from '@atlaskit/tooltip';
19
20
  import VisuallyHidden from '@atlaskit/visually-hidden';
21
+ import IconRenderer from '../icon-renderer';
20
22
 
21
23
  /**
22
24
  * Props from primitives that we explicitly ignore and remove from spread props,
@@ -177,7 +179,9 @@ export var ThemedButton = /*#__PURE__*/forwardRef(function ThemedButton(_ref4, r
177
179
  ref: ref
178
180
  }), IconBefore && /*#__PURE__*/React.createElement("span", {
179
181
  className: ax([textButtonStyles.iconBefore])
180
- }, /*#__PURE__*/React.createElement(IconBefore, {
182
+ }, fg('platform_themed_button_use_icon_renderer') ? /*#__PURE__*/React.createElement(IconRenderer, {
183
+ icon: IconBefore
184
+ }) : /*#__PURE__*/React.createElement(IconBefore, {
181
185
  label: "",
182
186
  color: "currentColor"
183
187
  })), /*#__PURE__*/React.createElement("span", {
@@ -270,7 +274,9 @@ export var ThemedIconButton = /*#__PURE__*/forwardRef(function ThemedIconButton(
270
274
  (_triggerProps$onBlur = triggerProps.onBlur) === null || _triggerProps$onBlur === void 0 || _triggerProps$onBlur.call(triggerProps, e);
271
275
  (_props$onBlur = props.onBlur) === null || _props$onBlur === void 0 || _props$onBlur.call(props, e);
272
276
  }
273
- }), /*#__PURE__*/React.createElement(Icon, {
277
+ }), fg('platform_themed_button_use_icon_renderer') ? /*#__PURE__*/React.createElement(IconRenderer, {
278
+ icon: Icon
279
+ }) : /*#__PURE__*/React.createElement(Icon, {
274
280
  label: "",
275
281
  color: "currentColor"
276
282
  }), /*#__PURE__*/React.createElement(VisuallyHidden, null, label))
@@ -9,7 +9,7 @@ export declare const gridRootId = "unsafe-design-system-page-layout-root";
9
9
  * The root component of the navigation system. It wraps the underlying components with the necessary contexts allowing to use certain data and hooks
10
10
  * @param skipLinksLabel - The very first element of the layout is a skip links container that can be accessed by pressing Tab button and holds the links to the other sections of the layout thus improving accessibility. This parameter defines the header text for this container
11
11
  */
12
- export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes, skipLinksLabel, testId, defaultSideNavCollapsed, }: {
12
+ export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes, skipLinksLabel, testId, defaultSideNavCollapsed, isSideNavShortcutEnabled, }: {
13
13
  /**
14
14
  * For rendering the layout areas, e.g. TopNav, SideNav, Main.
15
15
  * They should be rendered as immediate children.
@@ -47,4 +47,22 @@ export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes,
47
47
  * __Note:__ When provided, the `defaultCollapsed` props on `SideNav` and `SideNavToggleButton` will be ignored.
48
48
  */
49
49
  defaultSideNavCollapsed?: boolean;
50
+ /**
51
+ * Controls whether the side nav toggle shortcut is enabled. This will be used to bind the keyboard event listener,
52
+ * and to display the keyboard shortcuts in the appropriate tooltips (`SideNavToggleButton`, `SideNavPanelSplitter`).
53
+ *
54
+ * The shortcut key is `Ctrl` + `[`.
55
+ *
56
+ * The shortcut is not enabled by default.
57
+ *
58
+ * The shortcut will also be ignored if there are any open ADS modal dialogs (`@atlaskit/modal-dialog`). This is behind
59
+ * the `platform-dst-open-layer-observer-layer-type` feature flag.
60
+ *
61
+ * `SideNav` has another prop `canToggleWithShortcut()` that can be used to run additional checks after the shortcut
62
+ * is pressed, before the SideNav is toggled. You can use this to conditionally disable the shortcut based on your
63
+ * your own custom checks, e.g. if there is a legacy dialog open.
64
+ *
65
+ * Note: The built-in keyboard shortcut is behind the `navx-full-height-sidebar` feature flag.
66
+ */
67
+ isSideNavShortcutEnabled?: boolean;
50
68
  }): JSX.Element;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ /**
3
+ * Provider for the `IsSideNavShortcutEnabledContext`.
4
+ */
5
+ export declare function IsSideNavShortcutEnabledProvider({ children, isSideNavShortcutEnabled, }: {
6
+ children: React.ReactNode;
7
+ isSideNavShortcutEnabled: boolean;
8
+ }): React.JSX.Element;
9
+ /**
10
+ * Returns the value of the `isSideNavShortcutEnabled` prop from the `Root` component, which
11
+ * is shared through context.
12
+ */
13
+ export declare function useIsSideNavShortcutEnabled(): boolean;
@@ -63,6 +63,18 @@ type SideNavProps = CommonSlotProps & {
63
63
  onPeekEnd?: (args: {
64
64
  trigger: 'mouse-leave' | 'side-nav-expand';
65
65
  }) => void;
66
+ /**
67
+ * Whether the side nav should be toggled in response to the built-in keyboard shortcut. Use this callback to
68
+ * conditionally disable the shortcut based on your own custom checks, e.g. if there is a legacy dialog open.
69
+ *
70
+ * This prop will do nothing if `isSideNavShortcutEnabled` on Root is not set to `true`, as the keyboard event
71
+ * listener is only binded if `isSideNavShortcutEnabled` is `true`.
72
+ *
73
+ * The shortcut key is `Ctrl` + `[`.
74
+ *
75
+ * Note: The built-in keyboard shortcut is behind the `navx-full-height-sidebar` feature flag.
76
+ */
77
+ canToggleWithShortcut?: () => boolean;
66
78
  };
67
79
  export declare const onPeekStartDelayMs = 500;
68
80
  /**
@@ -75,5 +87,5 @@ export declare const onPeekStartDelayMs = 500;
75
87
  */
76
88
  export declare function SideNav({ children, defaultCollapsed, defaultWidth, testId, label, // Default value is defined in `SideNavInternal`
77
89
  skipLinkLabel, // Default value is defined in `SideNavInternal`
78
- onExpand, onCollapse, onPeekStart, onPeekEnd, id, }: SideNavProps): JSX.Element;
90
+ onExpand, onCollapse, onPeekStart, onPeekEnd, canToggleWithShortcut, id, }: SideNavProps): JSX.Element;
79
91
  export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Binds the keyboard shortcut to toggle the side nav.
3
+ */
4
+ export declare function useSideNavToggleKeyboardShortcut({ canToggleWithShortcut, }: {
5
+ canToggleWithShortcut?: () => boolean;
6
+ }): void;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { type IconProp } from '@atlaskit/button/new';
3
+ /**
4
+ * __Icon renderer__
5
+ *
6
+ * Used to support render props with icons.
7
+ *
8
+ * This is copied from @atlaskit/button to render IconButtons. Some IconButton's DOM element
9
+ * will be re-created during SSR -> SPA hydration without using this, which delays TTVC. As
10
+ * IconRenderer is already used in @atlaskit/button to mitigate this issue, add it here to
11
+ * mitigate the same issue for IconButtons in navigation-system, especially for ThemedIconButton.
12
+ *
13
+ */
14
+ declare const IconRenderer: ({ icon: Icon }: {
15
+ icon: IconProp;
16
+ }) => React.JSX.Element;
17
+ export default IconRenderer;
@@ -3,7 +3,7 @@
3
3
  * @jsx jsx
4
4
  */
5
5
  import React from 'react';
6
- import type { IconButtonProps } from '@atlaskit/button/new';
6
+ import { type IconButtonProps } from '@atlaskit/button/new';
7
7
  import { type AnchorProps, type PressableProps } from '@atlaskit/primitives/compiled';
8
8
  type ButtonAppearance = 'default' | 'primary' | 'subtle';
9
9
  interface CommonProps {
@@ -9,7 +9,7 @@ export declare const gridRootId = "unsafe-design-system-page-layout-root";
9
9
  * The root component of the navigation system. It wraps the underlying components with the necessary contexts allowing to use certain data and hooks
10
10
  * @param skipLinksLabel - The very first element of the layout is a skip links container that can be accessed by pressing Tab button and holds the links to the other sections of the layout thus improving accessibility. This parameter defines the header text for this container
11
11
  */
12
- export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes, skipLinksLabel, testId, defaultSideNavCollapsed, }: {
12
+ export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes, skipLinksLabel, testId, defaultSideNavCollapsed, isSideNavShortcutEnabled, }: {
13
13
  /**
14
14
  * For rendering the layout areas, e.g. TopNav, SideNav, Main.
15
15
  * They should be rendered as immediate children.
@@ -47,4 +47,22 @@ export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes,
47
47
  * __Note:__ When provided, the `defaultCollapsed` props on `SideNav` and `SideNavToggleButton` will be ignored.
48
48
  */
49
49
  defaultSideNavCollapsed?: boolean;
50
+ /**
51
+ * Controls whether the side nav toggle shortcut is enabled. This will be used to bind the keyboard event listener,
52
+ * and to display the keyboard shortcuts in the appropriate tooltips (`SideNavToggleButton`, `SideNavPanelSplitter`).
53
+ *
54
+ * The shortcut key is `Ctrl` + `[`.
55
+ *
56
+ * The shortcut is not enabled by default.
57
+ *
58
+ * The shortcut will also be ignored if there are any open ADS modal dialogs (`@atlaskit/modal-dialog`). This is behind
59
+ * the `platform-dst-open-layer-observer-layer-type` feature flag.
60
+ *
61
+ * `SideNav` has another prop `canToggleWithShortcut()` that can be used to run additional checks after the shortcut
62
+ * is pressed, before the SideNav is toggled. You can use this to conditionally disable the shortcut based on your
63
+ * your own custom checks, e.g. if there is a legacy dialog open.
64
+ *
65
+ * Note: The built-in keyboard shortcut is behind the `navx-full-height-sidebar` feature flag.
66
+ */
67
+ isSideNavShortcutEnabled?: boolean;
50
68
  }): JSX.Element;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ /**
3
+ * Provider for the `IsSideNavShortcutEnabledContext`.
4
+ */
5
+ export declare function IsSideNavShortcutEnabledProvider({ children, isSideNavShortcutEnabled, }: {
6
+ children: React.ReactNode;
7
+ isSideNavShortcutEnabled: boolean;
8
+ }): React.JSX.Element;
9
+ /**
10
+ * Returns the value of the `isSideNavShortcutEnabled` prop from the `Root` component, which
11
+ * is shared through context.
12
+ */
13
+ export declare function useIsSideNavShortcutEnabled(): boolean;
@@ -63,6 +63,18 @@ type SideNavProps = CommonSlotProps & {
63
63
  onPeekEnd?: (args: {
64
64
  trigger: 'mouse-leave' | 'side-nav-expand';
65
65
  }) => void;
66
+ /**
67
+ * Whether the side nav should be toggled in response to the built-in keyboard shortcut. Use this callback to
68
+ * conditionally disable the shortcut based on your own custom checks, e.g. if there is a legacy dialog open.
69
+ *
70
+ * This prop will do nothing if `isSideNavShortcutEnabled` on Root is not set to `true`, as the keyboard event
71
+ * listener is only binded if `isSideNavShortcutEnabled` is `true`.
72
+ *
73
+ * The shortcut key is `Ctrl` + `[`.
74
+ *
75
+ * Note: The built-in keyboard shortcut is behind the `navx-full-height-sidebar` feature flag.
76
+ */
77
+ canToggleWithShortcut?: () => boolean;
66
78
  };
67
79
  export declare const onPeekStartDelayMs = 500;
68
80
  /**
@@ -75,5 +87,5 @@ export declare const onPeekStartDelayMs = 500;
75
87
  */
76
88
  export declare function SideNav({ children, defaultCollapsed, defaultWidth, testId, label, // Default value is defined in `SideNavInternal`
77
89
  skipLinkLabel, // Default value is defined in `SideNavInternal`
78
- onExpand, onCollapse, onPeekStart, onPeekEnd, id, }: SideNavProps): JSX.Element;
90
+ onExpand, onCollapse, onPeekStart, onPeekEnd, canToggleWithShortcut, id, }: SideNavProps): JSX.Element;
79
91
  export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Binds the keyboard shortcut to toggle the side nav.
3
+ */
4
+ export declare function useSideNavToggleKeyboardShortcut({ canToggleWithShortcut, }: {
5
+ canToggleWithShortcut?: () => boolean;
6
+ }): void;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { type IconProp } from '@atlaskit/button/new';
3
+ /**
4
+ * __Icon renderer__
5
+ *
6
+ * Used to support render props with icons.
7
+ *
8
+ * This is copied from @atlaskit/button to render IconButtons. Some IconButton's DOM element
9
+ * will be re-created during SSR -> SPA hydration without using this, which delays TTVC. As
10
+ * IconRenderer is already used in @atlaskit/button to mitigate this issue, add it here to
11
+ * mitigate the same issue for IconButtons in navigation-system, especially for ThemedIconButton.
12
+ *
13
+ */
14
+ declare const IconRenderer: ({ icon: Icon }: {
15
+ icon: IconProp;
16
+ }) => React.JSX.Element;
17
+ export default IconRenderer;
@@ -3,7 +3,7 @@
3
3
  * @jsx jsx
4
4
  */
5
5
  import React from 'react';
6
- import type { IconButtonProps } from '@atlaskit/button/new';
6
+ import { type IconButtonProps } from '@atlaskit/button/new';
7
7
  import { type AnchorProps, type PressableProps } from '@atlaskit/primitives/compiled';
8
8
  type ButtonAppearance = 'default' | 'primary' | 'subtle';
9
9
  interface CommonProps {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/navigation-system",
3
- "version": "4.4.0",
3
+ "version": "4.5.1",
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",
@@ -72,7 +72,7 @@
72
72
  "@atlaskit/css": "^0.15.0",
73
73
  "@atlaskit/ds-lib": "^5.1.0",
74
74
  "@atlaskit/icon": "^28.5.0",
75
- "@atlaskit/layering": "^3.1.0",
75
+ "@atlaskit/layering": "^3.2.0",
76
76
  "@atlaskit/logo": "^19.9.0",
77
77
  "@atlaskit/platform-feature-flags": "^1.1.0",
78
78
  "@atlaskit/popup": "^4.4.0",
@@ -107,6 +107,7 @@
107
107
  "@atlaskit/link": "^3.2.0",
108
108
  "@atlaskit/lozenge": "^13.0.0",
109
109
  "@atlaskit/menu": "^8.4.0",
110
+ "@atlaskit/modal-dialog": "^14.6.0",
110
111
  "@atlaskit/onboarding": "^14.4.0",
111
112
  "@atlaskit/page-header": "^12.1.0",
112
113
  "@atlaskit/page-layout": "^4.2.0",
@@ -118,6 +119,7 @@
118
119
  "@atlassian/gemini": "^1.20.0",
119
120
  "@atlassian/search-dialog": "^9.7.0",
120
121
  "@atlassian/ssr-tests": "^0.3.0",
122
+ "@atlassian/testing-library": "^0.4.0",
121
123
  "@axe-core/playwright": "^4.8.0",
122
124
  "@testing-library/react": "^13.4.0",
123
125
  "@testing-library/react-hooks": "^8.0.1",
@@ -184,6 +186,15 @@
184
186
  },
185
187
  "platform_dst_side_nav_remove_custom_tooltip": {
186
188
  "type": "boolean"
189
+ },
190
+ "platform-dst-open-layer-observer-layer-type": {
191
+ "type": "boolean"
192
+ },
193
+ "platform_themed_button_use_icon_renderer": {
194
+ "type": "boolean"
195
+ },
196
+ "platform_dst_button_chevron_sizing": {
197
+ "type": "boolean"
187
198
  }
188
199
  },
189
200
  "homepage": "https://atlassian.design/components/navigation-system"