@atlaskit/navigation-system 0.177.2 → 0.178.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 (115) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/cjs/ui/menu-item/expandable-menu-item/expandable-menu-item-trigger.js +16 -3
  3. package/dist/cjs/ui/menu-item/menu-item.compiled.css +2 -2
  4. package/dist/cjs/ui/menu-item/menu-item.js +5 -4
  5. package/dist/es2019/ui/menu-item/expandable-menu-item/expandable-menu-item-trigger.js +17 -4
  6. package/dist/es2019/ui/menu-item/menu-item.compiled.css +2 -2
  7. package/dist/es2019/ui/menu-item/menu-item.js +4 -3
  8. package/dist/esm/ui/menu-item/expandable-menu-item/expandable-menu-item-trigger.js +17 -4
  9. package/dist/esm/ui/menu-item/menu-item.compiled.css +2 -2
  10. package/dist/esm/ui/menu-item/menu-item.js +5 -4
  11. package/dist/types/components/skip-links/skip-link.d.ts +1 -1
  12. package/dist/types/components/skip-links/skip-links-container.d.ts +1 -2
  13. package/dist/types/context/skip-links/skip-links-context.d.ts +15 -2
  14. package/dist/types/context/skip-links/skip-links-data-context.d.ts +0 -1
  15. package/dist/types/context/top-nav-start/top-nav-start-context.d.ts +0 -1
  16. package/dist/types/ui/menu-item/button-menu-item.d.ts +4 -4
  17. package/dist/types/ui/menu-item/container-avatar.d.ts +0 -1
  18. package/dist/types/ui/menu-item/drag-handle.d.ts +0 -1
  19. package/dist/types/ui/menu-item/expandable-menu-item/expandable-menu-item-context.d.ts +1 -2
  20. package/dist/types/ui/menu-item/expandable-menu-item/expandable-menu-item-level-context.d.ts +0 -1
  21. package/dist/types/ui/menu-item/expandable-menu-item/expandable-menu-item-trigger.d.ts +5 -5
  22. package/dist/types/ui/menu-item/flyout-menu-item/flyout-menu-item-context.d.ts +0 -1
  23. package/dist/types/ui/menu-item/flyout-menu-item/flyout-menu-item-trigger.d.ts +2 -2
  24. package/dist/types/ui/menu-item/link-menu-item.d.ts +6 -11
  25. package/dist/types/ui/menu-item/menu-item.d.ts +16 -11
  26. package/dist/types/ui/menu-item/menu-list-item.d.ts +1 -2
  27. package/dist/types/ui/menu-item/menu-list.d.ts +0 -1
  28. package/dist/types/ui/menu-item/top-level-spacer.d.ts +0 -1
  29. package/dist/types/ui/menu-item/use-scroll-menu-item-into-view.d.ts +0 -1
  30. package/dist/types/ui/menu-section/divider.d.ts +0 -1
  31. package/dist/types/ui/menu-section/menu-section-context.d.ts +0 -1
  32. package/dist/types/ui/page-layout/aside.d.ts +0 -1
  33. package/dist/types/ui/page-layout/banner.d.ts +0 -1
  34. package/dist/types/ui/page-layout/hoist-slot-sizes-context.d.ts +0 -1
  35. package/dist/types/ui/page-layout/hoist-utils.d.ts +2 -2
  36. package/dist/types/ui/page-layout/main/main-sticky-context.d.ts +0 -1
  37. package/dist/types/ui/page-layout/main/main.d.ts +0 -1
  38. package/dist/types/ui/page-layout/panel-splitter/get-width.d.ts +2 -2
  39. package/dist/types/ui/page-layout/panel.d.ts +0 -1
  40. package/dist/types/ui/page-layout/side-nav/side-nav-footer.d.ts +1 -1
  41. package/dist/types/ui/page-layout/side-nav/side-nav.d.ts +0 -1
  42. package/dist/types/ui/page-layout/side-nav/toggle-button-context.d.ts +0 -1
  43. package/dist/types/ui/page-layout/side-nav/toggle-button.d.ts +4 -4
  44. package/dist/types/ui/page-layout/top-nav/top-nav.d.ts +0 -1
  45. package/dist/types/ui/top-nav-items/chat-button.d.ts +3 -3
  46. package/dist/types/ui/top-nav-items/nav-logo/app-logo.d.ts +1 -1
  47. package/dist/types/ui/top-nav-items/nav-logo/custom-logo.d.ts +1 -1
  48. package/dist/types/ui/top-nav-items/search.d.ts +5 -5
  49. package/dist/types/ui/top-nav-items/themed/button.d.ts +2 -2
  50. package/dist/types/ui/top-nav-items/themed/has-custom-theme-context.d.ts +0 -1
  51. package/dist/types/ui/top-nav-items/themed/use-custom-theme.d.ts +0 -1
  52. package/dist/types-ts4.5/components/skip-links/skip-link.d.ts +1 -1
  53. package/dist/types-ts4.5/components/skip-links/skip-links-container.d.ts +1 -2
  54. package/dist/types-ts4.5/context/skip-links/skip-links-context.d.ts +15 -2
  55. package/dist/types-ts4.5/context/skip-links/skip-links-data-context.d.ts +0 -1
  56. package/dist/types-ts4.5/context/top-nav-start/top-nav-start-context.d.ts +0 -1
  57. package/dist/types-ts4.5/ui/menu-item/button-menu-item.d.ts +4 -4
  58. package/dist/types-ts4.5/ui/menu-item/container-avatar.d.ts +0 -1
  59. package/dist/types-ts4.5/ui/menu-item/drag-handle.d.ts +0 -1
  60. package/dist/types-ts4.5/ui/menu-item/expandable-menu-item/expandable-menu-item-context.d.ts +1 -2
  61. package/dist/types-ts4.5/ui/menu-item/expandable-menu-item/expandable-menu-item-level-context.d.ts +0 -1
  62. package/dist/types-ts4.5/ui/menu-item/expandable-menu-item/expandable-menu-item-trigger.d.ts +5 -5
  63. package/dist/types-ts4.5/ui/menu-item/flyout-menu-item/flyout-menu-item-context.d.ts +0 -1
  64. package/dist/types-ts4.5/ui/menu-item/flyout-menu-item/flyout-menu-item-trigger.d.ts +2 -2
  65. package/dist/types-ts4.5/ui/menu-item/link-menu-item.d.ts +6 -11
  66. package/dist/types-ts4.5/ui/menu-item/menu-item.d.ts +16 -11
  67. package/dist/types-ts4.5/ui/menu-item/menu-list-item.d.ts +1 -2
  68. package/dist/types-ts4.5/ui/menu-item/menu-list.d.ts +0 -1
  69. package/dist/types-ts4.5/ui/menu-item/top-level-spacer.d.ts +0 -1
  70. package/dist/types-ts4.5/ui/menu-item/use-scroll-menu-item-into-view.d.ts +0 -1
  71. package/dist/types-ts4.5/ui/menu-section/divider.d.ts +0 -1
  72. package/dist/types-ts4.5/ui/menu-section/menu-section-context.d.ts +0 -1
  73. package/dist/types-ts4.5/ui/page-layout/aside.d.ts +0 -1
  74. package/dist/types-ts4.5/ui/page-layout/banner.d.ts +0 -1
  75. package/dist/types-ts4.5/ui/page-layout/hoist-slot-sizes-context.d.ts +0 -1
  76. package/dist/types-ts4.5/ui/page-layout/hoist-utils.d.ts +2 -2
  77. package/dist/types-ts4.5/ui/page-layout/main/main-sticky-context.d.ts +0 -1
  78. package/dist/types-ts4.5/ui/page-layout/main/main.d.ts +0 -1
  79. package/dist/types-ts4.5/ui/page-layout/panel-splitter/get-width.d.ts +2 -2
  80. package/dist/types-ts4.5/ui/page-layout/panel.d.ts +0 -1
  81. package/dist/types-ts4.5/ui/page-layout/side-nav/side-nav-footer.d.ts +1 -1
  82. package/dist/types-ts4.5/ui/page-layout/side-nav/side-nav.d.ts +0 -1
  83. package/dist/types-ts4.5/ui/page-layout/side-nav/toggle-button-context.d.ts +0 -1
  84. package/dist/types-ts4.5/ui/page-layout/side-nav/toggle-button.d.ts +4 -4
  85. package/dist/types-ts4.5/ui/page-layout/top-nav/top-nav.d.ts +0 -1
  86. package/dist/types-ts4.5/ui/top-nav-items/chat-button.d.ts +3 -3
  87. package/dist/types-ts4.5/ui/top-nav-items/nav-logo/app-logo.d.ts +1 -1
  88. package/dist/types-ts4.5/ui/top-nav-items/nav-logo/custom-logo.d.ts +1 -1
  89. package/dist/types-ts4.5/ui/top-nav-items/search.d.ts +5 -5
  90. package/dist/types-ts4.5/ui/top-nav-items/themed/button.d.ts +2 -2
  91. package/dist/types-ts4.5/ui/top-nav-items/themed/has-custom-theme-context.d.ts +0 -1
  92. package/dist/types-ts4.5/ui/top-nav-items/themed/use-custom-theme.d.ts +0 -1
  93. package/examples/company-hub-mock.tsx +0 -4
  94. package/examples/confluence-mock.tsx +1 -4
  95. package/examples/drag-and-drop-jira-scaling-vr.tsx +25 -0
  96. package/examples/expandable-menu-item.tsx +1 -0
  97. package/examples/page-layout.tsx +0 -3
  98. package/package.json +5 -3
  99. package/src/__tests__/informational-vr-tests/__snapshots__/layering/layers-in-main-slot-short-viewport--desktop--platform-dst-nav4-layering-in-main-slot-fixes-false.png +0 -0
  100. package/src/__tests__/informational-vr-tests/__snapshots__/layering/layers-in-main-slot-short-viewport--desktop--platform-dst-nav4-layering-in-main-slot-fixes-true.png +0 -0
  101. package/src/__tests__/informational-vr-tests/__snapshots__/layering/side-nav-expanded-on-mobile--mobile.png +0 -0
  102. package/src/__tests__/informational-vr-tests/__snapshots__/layering/side-nav-expanded-on-mobile-without-panel--mobile.png +0 -0
  103. package/src/__tests__/informational-vr-tests/layering.vr.tsx +6 -0
  104. package/src/__tests__/vr-tests/__snapshots__/a11y-scaling/app--desktop.png +0 -0
  105. package/src/__tests__/vr-tests/a11y-scaling.vr.tsx +12 -0
  106. package/src/ui/menu-item/__tests__/playwright/scroll-into-view.spec.tsx +9 -10
  107. package/src/ui/menu-item/__tests__/unit/expandable-menu-item.test.tsx +88 -63
  108. package/src/ui/menu-item/__tests__/vr-tests/__snapshots__/menu-item/button-menu-item-example--desktop-webkit.png +0 -0
  109. package/src/ui/menu-item/__tests__/vr-tests/__snapshots__/menu-item/button-menu-item-rtlexample--desktop-webkit.png +0 -0
  110. package/src/ui/menu-item/__tests__/vr-tests/__snapshots__/menu-item/link-menu-item-example--desktop-webkit.png +0 -0
  111. package/src/ui/menu-item/__tests__/vr-tests/__snapshots__/menu-item/link-menu-item-rtlexample--desktop-webkit.png +0 -0
  112. package/src/ui/menu-item/__tests__/vr-tests/expandable.vr.tsx +8 -8
  113. package/src/ui/menu-item/expandable-menu-item/expandable-menu-item-trigger.tsx +19 -2
  114. package/src/ui/menu-item/menu-item.tsx +10 -8
  115. package/src/ui/page-layout/__tests__/unit/react-safety.test.tsx +0 -8
@@ -18,8 +18,8 @@ export declare const Search: ({ label, onClick, iconBefore: IconBefore, elemAfte
18
18
  * The icon component to render before the search input.
19
19
  */
20
20
  iconBefore?: React.ComponentType<NewIconProps & {
21
- spacing: 'spacious';
22
- }> | undefined;
21
+ spacing: "spacious";
22
+ }>;
23
23
  /**
24
24
  * The component to render after the search input.
25
25
  */
@@ -27,10 +27,10 @@ export declare const Search: ({ label, onClick, iconBefore: IconBefore, elemAfte
27
27
  /**
28
28
  * Handler called on click.
29
29
  */
30
- onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined;
30
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
31
31
  /**
32
32
  * An optional name used to identify events for [React UFO (Unified Frontend Observability) press interactions](https://developer.atlassian.com/platform/ufo/react-ufo/react-ufo/getting-started/#quick-start--press-interactions). For more information, see [React UFO integration into Design System components](https://go.atlassian.com/react-ufo-dst-integration).
33
33
  */
34
- interactionName?: string | undefined;
35
- 'aria-haspopup'?: React.AriaAttributes['aria-haspopup'];
34
+ interactionName?: string;
35
+ "aria-haspopup"?: React.AriaAttributes["aria-haspopup"];
36
36
  }) => JSX.Element;
@@ -88,7 +88,7 @@ export interface ThemedLinkButtonProps<RouterLinkConfig extends Record<string, a
88
88
  *
89
89
  * A themed link button for the top bar.
90
90
  */
91
- export declare const ThemedLinkButton: <RouterLinkConfig extends Record<string, any> = never>(props: ThemedLinkButtonProps<RouterLinkConfig> & React.RefAttributes<HTMLAnchorElement>) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
91
+ export declare const ThemedLinkButton: <RouterLinkConfig extends Record<string, any> = never>(props: ThemedLinkButtonProps<RouterLinkConfig> & React.RefAttributes<HTMLAnchorElement>) => React.ReactElement | null;
92
92
  /**
93
93
  * Props shared by `ThemedIconButtonProps` and `ThemedLinkIconButton`
94
94
  */
@@ -124,5 +124,5 @@ export interface ThemedLinkIconButtonProps<RouterLinkConfig extends Record<strin
124
124
  *
125
125
  * A themed link icon button for the top bar.
126
126
  */
127
- export declare const ThemedLinkIconButton: <RouterLinkConfig extends Record<string, any> = never>(props: ThemedLinkIconButtonProps<RouterLinkConfig> & React.RefAttributes<HTMLAnchorElement>) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
127
+ export declare const ThemedLinkIconButton: <RouterLinkConfig extends Record<string, any> = never>(props: ThemedLinkIconButtonProps<RouterLinkConfig> & React.RefAttributes<HTMLAnchorElement>) => React.ReactElement | null;
128
128
  export {};
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  /**
3
2
  * Context to let components know if a custom theme is being applied.
4
3
  */
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { type CustomTheme } from './get-custom-theme-styles';
3
2
  type Result = {
4
3
  isEnabled: false;
@@ -79,10 +79,6 @@ const contentStyles = cssMap({
79
79
  backgroundClip: 'content-box',
80
80
  paddingBlockEnd: token('space.800'),
81
81
  },
82
- heroImage: {
83
- width: '100%',
84
- objectFit: 'cover',
85
- },
86
82
  logo: {
87
83
  position: 'absolute',
88
84
  bottom: 0,
@@ -137,12 +137,9 @@ const contentStyles = cssMap({
137
137
  paddingBlock: token('space.400'),
138
138
  maxWidth: '760px',
139
139
  },
140
- title: {
141
- marginBottom: token('space.400'),
142
- },
143
140
  });
144
141
 
145
- export default function ConfluenceMockExample({}: {}) {
142
+ export default function ConfluenceMockExample() {
146
143
  return (
147
144
  <WithResponsiveViewport>
148
145
  <Root testId="root">
@@ -0,0 +1,25 @@
1
+ import React, { type ReactNode, useEffect } from 'react';
2
+
3
+ import { App } from './drag-and-drop/jira/entry';
4
+
5
+ const FontScaleContainer = ({ children }: { children: ReactNode }) => {
6
+ useEffect(() => {
7
+ const rootElement = document.querySelector('html');
8
+ if (rootElement) {
9
+ // Matches Chrome's "very large" font size setting
10
+ rootElement.style.fontSize = '24px';
11
+ }
12
+ }, []);
13
+
14
+ return children;
15
+ };
16
+
17
+ const AppScaled = () => {
18
+ return (
19
+ <FontScaleContainer>
20
+ <App />
21
+ </FontScaleContainer>
22
+ );
23
+ };
24
+
25
+ export default AppScaled;
@@ -591,6 +591,7 @@ export const ExpandableMenuItemWithAllOptions = () => (
591
591
  actionsOnHover={<MoreAction shouldRenderToParent />}
592
592
  elemAfter={<Lozenge>Elem after</Lozenge>}
593
593
  href="#"
594
+ testId="parent-menu-item"
594
595
  >
595
596
  Parent menu item
596
597
  </ExpandableMenuItemTrigger>
@@ -70,9 +70,6 @@ const styles = cssMap({
70
70
  position: 'sticky',
71
71
  insetBlockStart: token('space.150', '12px'),
72
72
  },
73
- hidden: {
74
- display: 'none',
75
- },
76
73
  legacyPositionedSibling: {
77
74
  position: 'absolute',
78
75
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/navigation-system",
3
- "version": "0.177.2",
3
+ "version": "0.178.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",
@@ -116,7 +116,7 @@
116
116
  "@atlaskit/button": "^23.3.0",
117
117
  "@atlaskit/css": "^0.12.0",
118
118
  "@atlaskit/ds-lib": "^5.0.0",
119
- "@atlaskit/icon": "^27.10.0",
119
+ "@atlaskit/icon": "^27.11.0",
120
120
  "@atlaskit/layering": "^3.0.0",
121
121
  "@atlaskit/logo": "^19.6.0",
122
122
  "@atlaskit/platform-feature-flags": "^1.1.0",
@@ -140,7 +140,6 @@
140
140
  "devDependencies": {
141
141
  "@af/accessibility-testing": "workspace:^",
142
142
  "@af/integration-testing": "workspace:^",
143
- "@af/react-unit-testing": "workspace:^",
144
143
  "@af/visual-regression": "workspace:^",
145
144
  "@atlaskit/app-provider": "^3.1.0",
146
145
  "@atlaskit/badge": "^18.1.0",
@@ -226,6 +225,9 @@
226
225
  },
227
226
  "platform_dst_nav4_flyout_use_capture_outside": {
228
227
  "type": "boolean"
228
+ },
229
+ "platform_dst_expandable_menu_item_elembefore_label": {
230
+ "type": "boolean"
229
231
  }
230
232
  },
231
233
  "homepage": "https://atlassian.design/components/navigation-system"
@@ -35,6 +35,9 @@ snapshotInformational(ScrollableVR, {
35
35
  description: 'Side nav expanded on mobile',
36
36
  prepare: async (page) => {
37
37
  await page.getByTestId('side-nav-toggle-button').click();
38
+
39
+ // Wait for the tooltip to be displayed, to reduce flakiness
40
+ await page.getByRole('tooltip').waitFor();
38
41
  },
39
42
  });
40
43
 
@@ -43,6 +46,9 @@ snapshotInformational(ScrollableNoPanelVR, {
43
46
  description: 'Side nav expanded on mobile without panel',
44
47
  prepare: async (page) => {
45
48
  await page.getByTestId('side-nav-toggle-button').click();
49
+
50
+ // Wait for the tooltip to be displayed, to reduce flakiness
51
+ await page.getByRole('tooltip').waitFor();
46
52
  },
47
53
  });
48
54
 
@@ -0,0 +1,12 @@
1
+ import { snapshot } from '@af/visual-regression';
2
+
3
+ import App from '../../../examples/drag-and-drop-jira-scaling-vr';
4
+
5
+ snapshot(App, {
6
+ variants: [
7
+ {
8
+ environment: { colorScheme: 'light' },
9
+ name: 'desktop',
10
+ },
11
+ ],
12
+ });
@@ -34,27 +34,26 @@ test.describe('scroll into view', () => {
34
34
  }) => {
35
35
  await page.setViewportSize(viewportSize);
36
36
 
37
- await page.visitExample('design-system', 'navigation-system', 'menu-item-scroll-into-view');
37
+ await page.visitExample('design-system', 'navigation-system', 'menu-item-scroll-into-view', {
38
+ featureFlag: 'platform_dst_expandable_menu_item_elembefore_label',
39
+ });
38
40
 
39
41
  // Collapse the "Teams" expandable menu item
40
42
  // We need to click on the collapse chevron icon button (instead of the menu item), so we only toggle the menu item and
41
43
  // don't *select* it too.
42
44
  const teamsMenuItemContainer = page.getByTestId('teams-menu-item-trigger-container');
43
45
  await teamsMenuItemContainer.scrollIntoViewIfNeeded();
44
- const teamsMenuItemCollapseButton = teamsMenuItemContainer.getByRole('button', {
45
- name: /Collapse/,
46
- });
47
- await teamsMenuItemCollapseButton.click();
46
+ const teamsMenuItemChevronButton = teamsMenuItemContainer.getByTestId(
47
+ 'teams-menu-item-trigger--elem-before-button',
48
+ );
49
+ await teamsMenuItemChevronButton.click();
48
50
 
49
51
  // Select the "Team 10" menu item (by clicking the button in the Main slot)
50
52
  const navigateToTeam10MenuItemButton = page.getByTestId('navigate-to-team-10-menu-item');
51
53
  await navigateToTeam10MenuItemButton.click();
52
54
 
53
- // Expand the "Teams" expandable menu item - using the same button as before, but now the label has changed to Expand
54
- const teamsMenuItemExpandButton = teamsMenuItemContainer.getByRole('button', {
55
- name: /Expand/,
56
- });
57
- await teamsMenuItemExpandButton.click();
55
+ // Expand the "Teams" expandable menu item - using the same button as before
56
+ await teamsMenuItemChevronButton.click();
58
57
 
59
58
  // The "Team 10" menu item should be scrolled into view
60
59
  const team10MenuItem = page.getByRole('link', { name: /Team 10/ });
@@ -7,6 +7,7 @@ import { IconButton } from '@atlaskit/button/new';
7
7
  import AddIcon from '@atlaskit/icon/core/add';
8
8
  import HomeIcon from '@atlaskit/icon/core/home';
9
9
  import MoreIcon from '@atlaskit/icon/core/show-more-horizontal';
10
+ import { ffTest } from '@atlassian/feature-flags-test-utils';
10
11
 
11
12
  import { ButtonMenuItem } from '../../button-menu-item';
12
13
  import { ExpandableMenuItem } from '../../expandable-menu-item/expandable-menu-item';
@@ -272,14 +273,16 @@ describe('ExpandableMenuItem', () => {
272
273
  it('should show the expandable content when the chevron icon button is clicked while collapsed', async () => {
273
274
  render(
274
275
  <ExpandableMenuItem>
275
- <ExpandableMenuItemTrigger href="/test">Parent menu item</ExpandableMenuItemTrigger>
276
+ <ExpandableMenuItemTrigger href="/test" testId="trigger-test-id">
277
+ Parent menu item
278
+ </ExpandableMenuItemTrigger>
276
279
  <ExpandableMenuItemContent>
277
280
  <ButtonMenuItem>Test expandable content</ButtonMenuItem>
278
281
  </ExpandableMenuItemContent>
279
282
  </ExpandableMenuItem>,
280
283
  );
281
284
 
282
- await userEvent.click(screen.getByRole('button', { name: /Expand/ }), {
285
+ await userEvent.click(screen.getByTestId('trigger-test-id--elem-before-button'), {
283
286
  // Skipping pointer events check as the `:not(:has(button,a))` selector
284
287
  // to disable pointer-events when there is no interactive child element
285
288
  // is not working correctly in our unit test environment
@@ -292,14 +295,16 @@ describe('ExpandableMenuItem', () => {
292
295
  it('should hide the expandable content when the chevron icon button is clicked while expanded', async () => {
293
296
  render(
294
297
  <ExpandableMenuItem isDefaultExpanded>
295
- <ExpandableMenuItemTrigger href="/test">Parent menu item</ExpandableMenuItemTrigger>
298
+ <ExpandableMenuItemTrigger href="/test" testId="trigger-test-id">
299
+ Parent menu item
300
+ </ExpandableMenuItemTrigger>
296
301
  <ExpandableMenuItemContent>
297
302
  <ButtonMenuItem>Test expandable content</ButtonMenuItem>
298
303
  </ExpandableMenuItemContent>
299
304
  </ExpandableMenuItem>,
300
305
  );
301
306
 
302
- await userEvent.click(screen.getByRole('button', { name: /Collapse/ }), {
307
+ await userEvent.click(screen.getByTestId('trigger-test-id--elem-before-button'), {
303
308
  // Skipping pointer events check as the `:not(:has(button,a))` selector
304
309
  // to disable pointer-events when there is no interactive child element
305
310
  // is not working correctly in our unit test environment
@@ -344,44 +349,6 @@ describe('ExpandableMenuItem', () => {
344
349
 
345
350
  expect(onClick).toHaveBeenCalled();
346
351
  });
347
-
348
- describe('aria-expanded', () => {
349
- const itemText = 'Parent menu item';
350
- const chevronExpandedText = 'Collapse';
351
- const chevronCollapsedText = 'Expand';
352
-
353
- it('should be `false` when collapsed', () => {
354
- render(
355
- <ExpandableMenuItem>
356
- <ExpandableMenuItemTrigger href="/test">{itemText}</ExpandableMenuItemTrigger>
357
- <ExpandableMenuItemContent>
358
- <ButtonMenuItem>Test expandable content</ButtonMenuItem>
359
- </ExpandableMenuItemContent>
360
- </ExpandableMenuItem>,
361
- );
362
-
363
- expect(screen.getByRole('button', { name: chevronCollapsedText })).toHaveAttribute(
364
- 'aria-expanded',
365
- 'false',
366
- );
367
- });
368
-
369
- it('should be `true` when expanded', () => {
370
- render(
371
- <ExpandableMenuItem isDefaultExpanded>
372
- <ExpandableMenuItemTrigger href="/test">{itemText}</ExpandableMenuItemTrigger>
373
- <ExpandableMenuItemContent>
374
- <ButtonMenuItem>Test expandable content</ButtonMenuItem>
375
- </ExpandableMenuItemContent>
376
- </ExpandableMenuItem>,
377
- );
378
-
379
- expect(screen.getByRole('button', { name: chevronExpandedText })).toHaveAttribute(
380
- 'aria-expanded',
381
- 'true',
382
- );
383
- });
384
- });
385
352
  });
386
353
 
387
354
  describe('when a href is not provided', () => {
@@ -597,36 +564,94 @@ describe('ExpandableMenuItemTrigger', () => {
597
564
  });
598
565
 
599
566
  describe('when a href is provided', () => {
600
- it('should display an icon button with correct label when expanded', () => {
567
+ it('should display an icon button with correct test id', () => {
601
568
  render(
602
- <ExpandableMenuItem isDefaultExpanded>
603
- <ExpandableMenuItemTrigger
604
- href="/test"
605
- elemBefore={<HomeIcon label="" />}
606
- actions={actions}
607
- >
569
+ <ExpandableMenuItem>
570
+ <ExpandableMenuItemTrigger href="/test" testId="trigger-test-id">
608
571
  Menu trigger
609
572
  </ExpandableMenuItemTrigger>
610
573
  </ExpandableMenuItem>,
611
574
  );
612
575
 
613
- expect(screen.getByRole('button', { name: /Collapse/ })).toBeVisible();
576
+ expect(screen.getByTestId('trigger-test-id--elem-before-button')).toBeVisible();
614
577
  });
615
578
 
616
- it('should display an icon button with correct label when collapsed', () => {
617
- render(
618
- <ExpandableMenuItem>
619
- <ExpandableMenuItemTrigger
620
- href="/test"
621
- elemBefore={<HomeIcon label="" />}
622
- actions={actions}
623
- >
624
- Menu trigger
625
- </ExpandableMenuItemTrigger>
626
- </ExpandableMenuItem>,
627
- );
579
+ ffTest.on('platform_dst_expandable_menu_item_elembefore_label', 'aria-expanded', () => {
580
+ it('should display an icon button with correct label and aria-expanded when expanded', () => {
581
+ render(
582
+ <ExpandableMenuItem isDefaultExpanded>
583
+ <ExpandableMenuItemTrigger
584
+ href="/test"
585
+ elemBefore={<HomeIcon label="" />}
586
+ actions={actions}
587
+ >
588
+ Menu trigger
589
+ </ExpandableMenuItemTrigger>
590
+ </ExpandableMenuItem>,
591
+ );
592
+
593
+ const chevronIconButton = screen.getByRole('button', { name: /Menu trigger/ });
594
+
595
+ expect(chevronIconButton).toBeVisible();
596
+ expect(chevronIconButton).toHaveAttribute('aria-expanded', 'true');
597
+ });
598
+
599
+ it('should display an icon button with correct label and aria-expanded when collapsed', () => {
600
+ render(
601
+ <ExpandableMenuItem>
602
+ <ExpandableMenuItemTrigger
603
+ href="/test"
604
+ elemBefore={<HomeIcon label="" />}
605
+ actions={actions}
606
+ >
607
+ Menu trigger
608
+ </ExpandableMenuItemTrigger>
609
+ </ExpandableMenuItem>,
610
+ );
611
+
612
+ const chevronIconButton = screen.getByRole('button', { name: /Menu trigger/ });
613
+
614
+ expect(chevronIconButton).toBeVisible();
615
+ expect(chevronIconButton).toHaveAttribute('aria-expanded', 'false');
616
+ });
617
+ });
618
+
619
+ ffTest.off('platform_dst_expandable_menu_item_elembefore_label', 'aria-expanded', () => {
620
+ const itemText = 'Parent menu item';
621
+ const chevronExpandedText = 'Collapse';
622
+ const chevronCollapsedText = 'Expand';
623
+
624
+ it('should be `false` when collapsed', () => {
625
+ render(
626
+ <ExpandableMenuItem>
627
+ <ExpandableMenuItemTrigger href="/test">{itemText}</ExpandableMenuItemTrigger>
628
+ <ExpandableMenuItemContent>
629
+ <ButtonMenuItem>Test expandable content</ButtonMenuItem>
630
+ </ExpandableMenuItemContent>
631
+ </ExpandableMenuItem>,
632
+ );
633
+
634
+ expect(screen.getByRole('button', { name: chevronCollapsedText })).toHaveAttribute(
635
+ 'aria-expanded',
636
+ 'false',
637
+ );
638
+ });
639
+
640
+ it('should be `true` when expanded', () => {
641
+ render(
642
+ <ExpandableMenuItem isDefaultExpanded>
643
+ <ExpandableMenuItemTrigger href="/test">{itemText}</ExpandableMenuItemTrigger>
644
+ <ExpandableMenuItemContent>
645
+ <ButtonMenuItem>Test expandable content</ButtonMenuItem>
646
+ </ExpandableMenuItemContent>
647
+ </ExpandableMenuItem>,
648
+ );
628
649
 
629
- expect(screen.getByRole('button', { name: /Expand/ })).toBeVisible();
650
+ expect(screen.getByRole('button', { name: chevronExpandedText })).toHaveAttribute(
651
+ 'aria-expanded',
652
+ 'true',
653
+ );
654
+ });
630
655
  });
631
656
 
632
657
  it('should display custom icon when not hovered over', () => {
@@ -190,13 +190,13 @@ snapshot(ExpandableMenuItemWithAllOptions, {
190
190
  {
191
191
  state: 'hovered',
192
192
  selector: {
193
- byRole: 'button',
194
- options: {
195
- name: 'Expand',
196
- },
193
+ byTestId: 'parent-menu-item--elem-before-button',
197
194
  },
198
195
  },
199
196
  ],
197
+ featureFlags: {
198
+ platform_dst_expandable_menu_item_elembefore_label: true,
199
+ },
200
200
  });
201
201
 
202
202
  snapshot(ExpandableMenuItemWithAllOptions, {
@@ -206,13 +206,13 @@ snapshot(ExpandableMenuItemWithAllOptions, {
206
206
  {
207
207
  state: 'focused',
208
208
  selector: {
209
- byRole: 'button',
210
- options: {
211
- name: 'Expand',
212
- },
209
+ byTestId: 'parent-menu-item--elem-before-button',
213
210
  },
214
211
  },
215
212
  ],
213
+ featureFlags: {
214
+ platform_dst_expandable_menu_item_elembefore_label: true,
215
+ },
216
216
  });
217
217
 
218
218
  snapshot(ExpandableMenuItemWithAllOptions, {
@@ -3,7 +3,7 @@
3
3
  * @jsxRuntime classic
4
4
  * @jsx jsx
5
5
  */
6
- import React, { forwardRef, type ReactNode, useCallback, useRef } from 'react';
6
+ import React, { forwardRef, type ReactNode, useCallback, useId, useRef } from 'react';
7
7
 
8
8
  import { cssMap, jsx } from '@compiled/react';
9
9
 
@@ -12,6 +12,7 @@ import { IconButton } from '@atlaskit/button/new';
12
12
  import type { IconProps } from '@atlaskit/icon';
13
13
  import ChevronDownIcon from '@atlaskit/icon/core/chevron-down';
14
14
  import ChevronRightIcon from '@atlaskit/icon/core/chevron-right';
15
+ import { fg } from '@atlaskit/platform-feature-flags';
15
16
  import { token } from '@atlaskit/tokens';
16
17
 
17
18
  import { MenuItemBase, nestedOpenPopupCSSSelector } from '../menu-item';
@@ -229,6 +230,7 @@ export const ExpandableMenuItemTrigger = forwardRef<
229
230
  },
230
231
  forwardedRef,
231
232
  ) => {
233
+ const id = useId();
232
234
  const onExpansionToggle = useOnExpansionToggle();
233
235
  const isExpanded = useIsExpanded();
234
236
  const setIsExpanded = useSetIsExpanded();
@@ -271,11 +273,25 @@ export const ExpandableMenuItemTrigger = forwardRef<
271
273
  />
272
274
  )}
273
275
  aria-expanded={isExpanded}
274
- label={isExpanded ? 'Collapse' : 'Expand'}
276
+ // We are labelling the icon button using the containing menu item's content, to provide context to
277
+ // screen readers on what will actually be expanded or collapsed. Screen readers will also use the
278
+ // `aria-expanded` attribute to indicate the expanded state of the menu item.
279
+ // We are not using the `aria-label` attribute here as it is not supported by the `IconButton` component.
280
+ aria-labelledby={fg('platform_dst_expandable_menu_item_elembefore_label') ? id : undefined}
281
+ // IconButton requires a label prop, however it will not be used by screen readers as we are setting
282
+ // `aria-labelledby`, which will be used instead.
283
+ label={
284
+ fg('platform_dst_expandable_menu_item_elembefore_label')
285
+ ? ''
286
+ : isExpanded
287
+ ? 'Collapse'
288
+ : 'Expand'
289
+ }
275
290
  appearance="subtle"
276
291
  spacing="compact"
277
292
  onClick={handleIconClick}
278
293
  interactionName={interactionName}
294
+ testId={testId ? `${testId}--elem-before-button` : undefined}
279
295
  />
280
296
  ) : (
281
297
  <ExpandableMenuItemIcon
@@ -292,6 +308,7 @@ export const ExpandableMenuItemTrigger = forwardRef<
292
308
  ref={itemRef}
293
309
  >
294
310
  <MenuItemBase
311
+ id={id}
295
312
  actions={actions}
296
313
  actionsOnHover={actionsOnHover}
297
314
  elemBefore={elemBefore}
@@ -216,7 +216,8 @@ const containerStyles = cssMap({
216
216
  * are unusable.
217
217
  */
218
218
  minWidth: '72px',
219
- height: '32px',
219
+ // Using rem so it scales with browser font size and rem-based spacing/typography
220
+ height: '2rem',
220
221
  alignItems: 'center',
221
222
  userSelect: 'none',
222
223
  borderRadius: token('border.radius'),
@@ -246,12 +247,6 @@ const containerStyles = cssMap({
246
247
  showHoverActions: {
247
248
  [actionsOnHoverDisplayVar]: 'flex',
248
249
  },
249
- removeElemAfterOnHover: {
250
- // On hover of the menu item, remove the elemAfter
251
- '&:hover, &:focus-within': {
252
- [elemAfterDisplayVar]: 'none',
253
- },
254
- },
255
250
  removeElemAfterOnHoverOrOpenNestedPopup: {
256
251
  // On hover of the menu item, remove the elemAfter
257
252
  '&:hover, &:focus-within': {
@@ -286,7 +281,7 @@ const containerStyles = cssMap({
286
281
  },
287
282
  hasDescription: {
288
283
  /* Standard 32px + another 16px for the description */
289
- height: '48px',
284
+ height: '3rem',
290
285
  },
291
286
  dragging: {
292
287
  opacity: 0.4,
@@ -524,6 +519,11 @@ function getTextColor({
524
519
  */
525
520
  type MenuItemBaseProps<T extends HTMLAnchorElement | HTMLButtonElement> =
526
521
  MenuItemLinkOrButtonCommonProps & {
522
+ /**
523
+ * ID attribute, passed to the interactive element (anchor/button). This is not publicly exposed, and is currently only
524
+ * used internally by `ExpandableMenuItemTrigger` for the `aria-labelledby` attribute.
525
+ */
526
+ id?: string;
527
527
  href?: string | Record<string, any>;
528
528
  target?: HTMLAnchorElement['target'];
529
529
  isDisabled?: boolean;
@@ -545,6 +545,7 @@ type MenuItemBaseProps<T extends HTMLAnchorElement | HTMLButtonElement> =
545
545
  */
546
546
  const MenuItemBaseNoRef = <T extends HTMLAnchorElement | HTMLButtonElement>(
547
547
  {
548
+ id,
548
549
  testId,
549
550
  actions,
550
551
  actionsOnHover,
@@ -736,6 +737,7 @@ const MenuItemBaseNoRef = <T extends HTMLAnchorElement | HTMLButtonElement>(
736
737
  'aria-controls': ariaControls,
737
738
  'aria-haspopup': ariaHasPopup,
738
739
  ref: mergeRefs([forwardedRef, tooltipProps.ref]),
740
+ id,
739
741
  testId,
740
742
  interactionName,
741
743
  };
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
2
 
3
- import { toBeSuspendable, toPassStrictMode } from '@af/react-unit-testing';
4
-
5
3
  import CompositionExample from '../../../../../examples/composition';
6
4
 
7
5
  import { resetMatchMedia } from './_test-utils';
@@ -10,12 +8,6 @@ beforeEach(() => {
10
8
  resetMatchMedia();
11
9
  });
12
10
 
13
- // TODO: move to `jestFrameworkSetup.js` in follow up pull request
14
- expect.extend({
15
- toBeSuspendable,
16
- toPassStrictMode,
17
- });
18
-
19
11
  it('should support being suspended', async () => {
20
12
  await expect(() => <CompositionExample />).toBeSuspendable();
21
13
  });