@kaizen/components 0.0.0-canary-guidance-block-codemod-20251215033932 → 0.0.0-canary-01-titleblock-20251216220648

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 (40) hide show
  1. package/codemods/migrateGuidanceBlockActionsToActionsSlot/transformActionsToActionsSlot.spec.ts +105 -0
  2. package/codemods/utils/transformV1ButtonPropsToButtonOrLinkButton.ts +4 -14
  3. package/dist/cjs/src/MenuV1/index.cjs +4 -3
  4. package/dist/cjs/src/TitleBlock/TitleBlock.cjs +32 -36
  5. package/dist/cjs/src/TitleBlock/TitleBlock.module.scss.cjs +5 -1
  6. package/dist/cjs/src/TitleBlock/subcomponents/MainActions.cjs +90 -45
  7. package/dist/cjs/src/TitleBlock/subcomponents/MainActions.module.scss.cjs +3 -1
  8. package/dist/cjs/src/TitleBlock/subcomponents/SecondaryActions.cjs +51 -14
  9. package/dist/esm/src/MenuV1/index.mjs +5 -3
  10. package/dist/esm/src/TitleBlock/TitleBlock.mjs +33 -37
  11. package/dist/esm/src/TitleBlock/TitleBlock.module.scss.mjs +5 -1
  12. package/dist/esm/src/TitleBlock/subcomponents/MainActions.mjs +92 -47
  13. package/dist/esm/src/TitleBlock/subcomponents/MainActions.module.scss.mjs +3 -1
  14. package/dist/esm/src/TitleBlock/subcomponents/SecondaryActions.mjs +51 -14
  15. package/dist/styles.css +89 -210
  16. package/dist/types/TitleBlock/TitleBlock.d.ts +1 -1
  17. package/dist/types/TitleBlock/subcomponents/MainActions.d.ts +4 -3
  18. package/package.json +1 -1
  19. package/src/Avatar/Avatar.module.css +1 -0
  20. package/src/TitleBlock/TitleBlock.module.scss +63 -19
  21. package/src/TitleBlock/TitleBlock.spec.tsx +33 -461
  22. package/src/TitleBlock/TitleBlock.tsx +7 -24
  23. package/src/TitleBlock/_docs/TitleBlock.stories.tsx +59 -12
  24. package/src/TitleBlock/_mixins.scss +6 -0
  25. package/src/TitleBlock/subcomponents/MainActions.module.scss +20 -2
  26. package/src/TitleBlock/subcomponents/MainActions.tsx +127 -70
  27. package/src/TitleBlock/subcomponents/SecondaryActions.tsx +105 -45
  28. package/src/TitleBlock/subcomponents/Toolbar.tsx +1 -0
  29. package/dist/cjs/src/MenuV1/subcomponents/MenuHeading/MenuHeading.cjs +0 -27
  30. package/dist/cjs/src/MenuV1/subcomponents/MenuHeading/MenuHeading.module.scss.cjs +0 -6
  31. package/dist/cjs/src/TitleBlock/subcomponents/MobileActions.cjs +0 -306
  32. package/dist/cjs/src/TitleBlock/subcomponents/MobileActions.module.scss.cjs +0 -16
  33. package/dist/esm/src/MenuV1/subcomponents/MenuHeading/MenuHeading.mjs +0 -21
  34. package/dist/esm/src/MenuV1/subcomponents/MenuHeading/MenuHeading.module.scss.mjs +0 -4
  35. package/dist/esm/src/TitleBlock/subcomponents/MobileActions.mjs +0 -300
  36. package/dist/esm/src/TitleBlock/subcomponents/MobileActions.module.scss.mjs +0 -14
  37. package/dist/types/TitleBlock/subcomponents/MobileActions.d.ts +0 -14
  38. package/src/TitleBlock/subcomponents/MobileActions.module.scss +0 -208
  39. package/src/TitleBlock/subcomponents/MobileActions.spec.tsx +0 -210
  40. package/src/TitleBlock/subcomponents/MobileActions.tsx +0 -472
@@ -8,7 +8,6 @@ import { Select } from '~components/Select'
8
8
  import { Tag } from '~components/Tag'
9
9
  import { useMediaQueries } from '~components/utils/useMediaQueries'
10
10
  import { MainActions } from './subcomponents/MainActions'
11
- import { MobileActions } from './subcomponents/MobileActions'
12
11
  import { SecondaryActions } from './subcomponents/SecondaryActions'
13
12
  import {
14
13
  type NavigationTabs,
@@ -18,7 +17,7 @@ import {
18
17
  type TitleBlockProps,
19
18
  type TitleBlockVariant,
20
19
  } from './types'
21
- import { createTabletOverflowMenuItems, isReversed } from './utils'
20
+ import { isReversed } from './utils'
22
21
  import styles from './TitleBlock.module.scss'
23
22
 
24
23
  const renderTag = (surveyStatus: SurveyStatus): ReactNode => {
@@ -156,6 +155,9 @@ const Breadcrumb = ({
156
155
  <div className={styles.circle}>
157
156
  <Icon name="arrow_back" isPresentational shouldMirrorInRTL />
158
157
  </div>
158
+ <div className={styles.staticIcon}>
159
+ <Icon name="arrow_back" isPresentational shouldMirrorInRTL />
160
+ </div>
159
161
  <span
160
162
  className={styles.breadcrumbTextLink}
161
163
  data-automation-id={textAutomationId}
@@ -258,7 +260,6 @@ export const TitleBlock = ({
258
260
  sectionTitleDescriptionAutomationId = 'TitleBlock__SectionTitleDescription',
259
261
  breadcrumbAutomationId = 'TitleBlock__Breadcrumb',
260
262
  breadcrumbTextAutomationId = 'TitleBlock__BreadcrumbText',
261
- autoHideMobileActionsMenu = false,
262
263
  }: TitleBlockProps): JSX.Element => {
263
264
  const hasNavigationTabs = navigationTabs && navigationTabs.length > 0
264
265
  const collapseNavigationArea =
@@ -343,19 +344,13 @@ export const TitleBlock = ({
343
344
  </>
344
345
  </div>
345
346
  </div>
346
- {(primaryAction ??
347
- defaultAction ??
348
- secondaryActions ??
349
- secondaryOverflowMenuItems) && (
347
+ {(primaryAction ?? defaultAction ?? secondaryActions) && (
350
348
  <MainActions
351
349
  primaryAction={primaryAction}
352
350
  defaultAction={defaultAction}
351
+ secondaryActions={secondaryActions}
352
+ secondaryOverflowMenuItems={secondaryOverflowMenuItems}
353
353
  reversed={isReversed(variant)}
354
- overflowMenuItems={createTabletOverflowMenuItems(
355
- secondaryActions,
356
- secondaryOverflowMenuItems,
357
- )}
358
- showOverflowMenu={isSmallOrMediumViewport}
359
354
  />
360
355
  )}
361
356
  </div>
@@ -395,18 +390,6 @@ export const TitleBlock = ({
395
390
  </div>
396
391
  </div>
397
392
  </div>
398
- <MobileActions
399
- primaryAction={primaryAction}
400
- defaultAction={defaultAction}
401
- secondaryActions={secondaryActions}
402
- secondaryOverflowMenuItems={secondaryOverflowMenuItems}
403
- drawerHandleLabelIconPosition={
404
- primaryAction && 'iconPosition' in primaryAction
405
- ? primaryAction.iconPosition
406
- : undefined
407
- }
408
- autoHide={autoHideMobileActionsMenu}
409
- />
410
393
  </div>
411
394
  </>
412
395
  )
@@ -34,7 +34,7 @@ const meta = {
34
34
  layout: 'fullscreen',
35
35
  },
36
36
  args: {
37
- title: 'Page title',
37
+ title: 'Page title Page title Page title ',
38
38
  surveyStatus: { text: 'Due July 8, 2030', status: 'default' },
39
39
  avatar: {
40
40
  avatarSrc: assetUrl('site/empty-state.png'),
@@ -112,15 +112,42 @@ export const Viewports: Story = {
112
112
  type: 'desktop',
113
113
  },
114
114
  },
115
- defaultViewport: 'default',
115
+ // defaultViewport: 'default',
116
116
  },
117
117
  chromatic: {
118
118
  disable: false,
119
- viewports: [1079, 1365, 1366],
119
+ viewports: [1079, 1365, 1366, 760, 360],
120
120
  },
121
121
  },
122
122
  }
123
123
 
124
+ export const WithNoSecondaryOverflow: Story = {
125
+ parameters: {
126
+ viewport: {
127
+ viewports: {
128
+ default: {
129
+ name: 'Above or equal to 1366',
130
+ styles: { width: '1366px', height: '800px' },
131
+ type: 'desktop',
132
+ },
133
+ under1366: {
134
+ name: 'Under 1366',
135
+ styles: { width: '1365px', height: '800px' },
136
+ type: 'desktop',
137
+ },
138
+ },
139
+ // defaultViewport: 'default',
140
+ },
141
+ chromatic: {
142
+ disable: false,
143
+ viewports: [1365, 1366, 760, 360],
144
+ },
145
+ },
146
+ args: {
147
+ title: 'Test no secondary overflow menu items',
148
+ secondaryOverflowMenuItems: undefined,
149
+ },
150
+ }
124
151
  export const HasLongTitle: Story = {
125
152
  parameters: {
126
153
  viewport: {
@@ -136,11 +163,11 @@ export const HasLongTitle: Story = {
136
163
  type: 'desktop',
137
164
  },
138
165
  },
139
- defaultViewport: 'default',
166
+ // defaultViewport: 'default',
140
167
  },
141
168
  chromatic: {
142
169
  disable: false,
143
- viewports: [1365, 1366],
170
+ viewports: [1365, 1366, 760, 360],
144
171
  },
145
172
  },
146
173
  args: { title: 'A long title with over thirty characters' },
@@ -156,21 +183,41 @@ export const LightVariant: Story = {
156
183
  viewport: {
157
184
  viewports: {
158
185
  default: {
159
- name: 'Above or equal to 1366',
186
+ name: 'desktop (above or equal to 1366)',
160
187
  styles: { width: '1366px', height: '800px' },
161
188
  type: 'desktop',
162
189
  },
163
- under1366: {
164
- name: 'Under 1366',
190
+ desktopSm: {
191
+ name: 'desktop-sm (1024 - 1365)',
165
192
  styles: { width: '1365px', height: '800px' },
166
193
  type: 'desktop',
167
194
  },
195
+ tablet: {
196
+ name: 'tablet (672 - 1023)',
197
+ styles: { width: '1023px', height: '800px' },
198
+ type: 'desktop',
199
+ },
200
+ mobileResponsive: {
201
+ name: 'mobile-responsive (512 - 671)',
202
+ styles: { width: '671px', height: '800px' },
203
+ type: 'desktop',
204
+ },
205
+ mobile: {
206
+ name: 'mobile (361 - 512)',
207
+ styles: { width: '511px', height: '800px' },
208
+ type: 'desktop',
209
+ },
210
+ mobileXs: {
211
+ name: 'mobile (under 360)',
212
+ styles: { width: '360px', height: '800px' },
213
+ type: 'desktop',
214
+ },
168
215
  },
169
- defaultViewport: 'default',
216
+ // defaultViewport: 'default',
170
217
  },
171
218
  chromatic: {
172
219
  disable: false,
173
- viewports: [1365, 1366],
220
+ viewports: [1365, 1366, 760, 360],
174
221
  },
175
222
  },
176
223
  args: {
@@ -202,7 +249,7 @@ export const AdminVariant: Story = {
202
249
  type: 'desktop',
203
250
  },
204
251
  },
205
- defaultViewport: 'default',
252
+ // defaultViewport: 'default',
206
253
  },
207
254
  chromatic: {
208
255
  disable: false,
@@ -237,7 +284,7 @@ export const EducationVariant: Story = {
237
284
  type: 'desktop',
238
285
  },
239
286
  },
240
- defaultViewport: 'default',
287
+ // defaultViewport: 'default',
241
288
  },
242
289
  chromatic: {
243
290
  disable: false,
@@ -35,3 +35,9 @@
35
35
  @content;
36
36
  }
37
37
  }
38
+
39
+ @mixin title-block-xsmall {
40
+ @container (max-width: 511px) {
41
+ @content;
42
+ }
43
+ }
@@ -8,11 +8,29 @@
8
8
  justify-content: flex-end;
9
9
  flex-grow: 1;
10
10
  flex-shrink: 0;
11
+ white-space: nowrap;
11
12
 
12
- @include ca-margin($start: calc(#{$ca-grid} / 2));
13
+ .defaultActionButtonMinimized {
14
+ display: none;
15
+ margin-inline-end: 0;
16
+ }
13
17
 
14
18
  @include title-block-small {
15
- display: none;
19
+ justify-content: flex-start;
20
+ padding-left: 2.75rem;
21
+
22
+ @media (max-width: 360px) {
23
+ .defaultActionButton {
24
+ display: none;
25
+ margin-inline-end: 0;
26
+ }
27
+
28
+ .defaultActionButtonMinimized {
29
+ display: flex;
30
+ margin-inline-end: 3px;
31
+ margin-left: -0.5rem;
32
+ }
33
+ }
16
34
  }
17
35
  }
18
36
  }
@@ -2,13 +2,14 @@ import React from 'react'
2
2
  import { Button, IconButton } from '~components/ButtonV1'
3
3
  import { Icon } from '~components/Icon'
4
4
  import { Menu, MenuList } from '~components/MenuV1'
5
- import { TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID } from '../constants'
5
+ import { TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID } from '~components/TitleBlock'
6
6
  import {
7
7
  type DefaultActionProps,
8
8
  type PrimaryActionProps,
9
+ type SecondaryActionsProps,
9
10
  type TitleBlockMenuItemProps,
10
11
  } from '../types'
11
- import { isMenuGroupNotButton } from '../utils'
12
+ import { createTabletOverflowMenuItems, isMenuGroupNotButton } from '../utils'
12
13
  import { TitleBlockMenuItem } from './TitleBlockMenuItem'
13
14
  import { Toolbar } from './Toolbar'
14
15
  import styles from './MainActions.module.scss'
@@ -17,18 +18,135 @@ type MainActionsProps = {
17
18
  primaryAction?: PrimaryActionProps
18
19
  defaultAction?: DefaultActionProps
19
20
  reversed?: boolean
20
- overflowMenuItems?: TitleBlockMenuItemProps[]
21
+ secondaryActions?: SecondaryActionsProps
22
+ secondaryOverflowMenuItems?: TitleBlockMenuItemProps[]
21
23
  showOverflowMenu?: boolean
22
24
  }
23
25
 
26
+ const renderSecondaryAndOverflowMenu = (
27
+ secondaryOverflowMenuItems?: TitleBlockMenuItemProps[],
28
+ reversed?: boolean,
29
+ ): JSX.Element | undefined => {
30
+ if (!secondaryOverflowMenuItems) return undefined
31
+ return (
32
+ <Menu
33
+ align="right"
34
+ button={
35
+ <IconButton
36
+ label={'Open secondary and overflow menu'}
37
+ reversed={reversed}
38
+ icon={<Icon name="more_horiz" isPresentational />}
39
+ id={TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}
40
+ classNameOverride={styles.overflowMenuButton}
41
+ />
42
+ }
43
+ >
44
+ <MenuList>
45
+ {secondaryOverflowMenuItems.map((menuItem, i) => (
46
+ <TitleBlockMenuItem key={i} {...menuItem} />
47
+ ))}
48
+ </MenuList>
49
+ </Menu>
50
+ )
51
+ }
52
+
24
53
  export const MainActions = ({
25
54
  primaryAction,
26
55
  defaultAction,
56
+ secondaryActions,
57
+ secondaryOverflowMenuItems,
27
58
  reversed = false,
28
- overflowMenuItems,
29
- showOverflowMenu = false,
30
59
  }: MainActionsProps): JSX.Element => {
31
60
  let items
61
+
62
+ // Build combined secondary + overflow menu items once, to be spread into the toolbar items
63
+ const secondaryAndOverflowMenu: { key: string; node: JSX.Element }[] = []
64
+
65
+ // Convert defaultAction to menu item format and prepend to combined list
66
+ let defaultActionMenuItem: TitleBlockMenuItemProps | undefined
67
+ if (defaultAction) {
68
+ if ('component' in defaultAction) {
69
+ defaultActionMenuItem = defaultAction
70
+ } else if ('onClick' in defaultAction) {
71
+ defaultActionMenuItem = {
72
+ label: defaultAction.label,
73
+ icon: defaultAction.icon,
74
+ onClick: defaultAction.onClick,
75
+ disabled: defaultAction.disabled,
76
+ }
77
+ } else if ('href' in defaultAction) {
78
+ defaultActionMenuItem = {
79
+ label: defaultAction.label,
80
+ icon: defaultAction.icon,
81
+ href: defaultAction.href,
82
+ disabled: defaultAction.disabled,
83
+ }
84
+ }
85
+ }
86
+
87
+ const combinedSecondaryOverflowItems = createTabletOverflowMenuItems(
88
+ secondaryActions,
89
+ secondaryOverflowMenuItems,
90
+ )
91
+
92
+ // Prepend defaultAction to the combined list if it exists
93
+ const allMenuItems = defaultActionMenuItem
94
+ ? [defaultActionMenuItem, ...combinedSecondaryOverflowItems]
95
+ : combinedSecondaryOverflowItems
96
+
97
+ if (allMenuItems.length > 0) {
98
+ secondaryAndOverflowMenu.push({
99
+ key: 'overflowMenu',
100
+ node: renderSecondaryAndOverflowMenu(allMenuItems, reversed)!,
101
+ })
102
+ }
103
+
104
+ const defaultActionItem = defaultAction
105
+ ? [
106
+ {
107
+ key: 'defaultAction',
108
+ node: (
109
+ <Button
110
+ classNameOverride={styles.defaultActionButton}
111
+ {...{
112
+ ...defaultAction,
113
+ reversed: defaultAction.reversed ?? reversed,
114
+ }}
115
+ data-automation-id="title-block-default-action-button"
116
+ data-testid="title-block-default-action-button"
117
+ />
118
+ ),
119
+ },
120
+ ]
121
+ : []
122
+
123
+ const defaultActionItemMinimized = defaultAction
124
+ ? [
125
+ {
126
+ key: 'defaultActionMinimized',
127
+ node: (
128
+ <div className={styles.defaultActionButtonMinimized}>
129
+ <Menu
130
+ align="right"
131
+ button={
132
+ <IconButton
133
+ label={'Open default action overflow menu'}
134
+ reversed={reversed}
135
+ icon={<Icon name="more_horiz" isPresentational />}
136
+ id={TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}
137
+ />
138
+ }
139
+ >
140
+ <MenuList>
141
+ {defaultActionMenuItem && <TitleBlockMenuItem {...defaultActionMenuItem} />}
142
+ </MenuList>
143
+ </Menu>
144
+ </div>
145
+ ),
146
+ },
147
+ ]
148
+ : []
149
+
32
150
  if (primaryAction && isMenuGroupNotButton(primaryAction)) {
33
151
  const menuContent = primaryAction.menuItems.map((item, idx) => (
34
152
  <TitleBlockMenuItem
@@ -40,23 +158,8 @@ export const MainActions = ({
40
158
  ))
41
159
 
42
160
  items = [
43
- ...(defaultAction
44
- ? [
45
- {
46
- key: 'defaultAction',
47
- node: (
48
- <Button
49
- {...{
50
- ...defaultAction,
51
- reversed: defaultAction.reversed ?? reversed,
52
- }}
53
- data-automation-id="title-block-default-action-button"
54
- data-testid="title-block-default-action-button"
55
- />
56
- ),
57
- },
58
- ]
59
- : []),
161
+ ...defaultActionItem,
162
+ ...defaultActionItemMinimized,
60
163
  ...(primaryAction
61
164
  ? [
62
165
  {
@@ -93,23 +196,8 @@ export const MainActions = ({
93
196
  ]
94
197
  } else {
95
198
  items = [
96
- ...(defaultAction
97
- ? [
98
- {
99
- key: 'defaultAction',
100
- node: (
101
- <Button
102
- {...{
103
- ...defaultAction,
104
- reversed: defaultAction.reversed ?? reversed,
105
- }}
106
- data-automation-id="title-block-default-action-button"
107
- data-testid="title-block-default-action-button"
108
- />
109
- ),
110
- },
111
- ]
112
- : []),
199
+ ...defaultActionItem,
200
+ ...defaultActionItemMinimized,
113
201
  ...(primaryAction
114
202
  ? [
115
203
  {
@@ -139,37 +227,6 @@ export const MainActions = ({
139
227
  ]
140
228
  }
141
229
 
142
- if (overflowMenuItems && showOverflowMenu && overflowMenuItems.length > 0) {
143
- items = [
144
- {
145
- key: 'overflowMenu',
146
- node: (
147
- <Menu
148
- align="right"
149
- button={
150
- <IconButton
151
- id={TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}
152
- label="Open secondary menu"
153
- reversed={reversed}
154
- icon={<Icon name="more_horiz" isPresentational />}
155
- />
156
- }
157
- >
158
- <MenuList>
159
- {overflowMenuItems.map((menuItem, idx) => (
160
- <TitleBlockMenuItem
161
- {...menuItem}
162
- key={`main-action-overflow-item-menu-item-${idx}`}
163
- />
164
- ))}
165
- </MenuList>
166
- </Menu>
167
- ),
168
- },
169
- ...items,
170
- ]
171
- }
172
-
173
230
  return (
174
231
  <div className={styles.mainActionsContainer}>
175
232
  <Toolbar
@@ -5,6 +5,7 @@ import { Menu, MenuList } from '~components/MenuV1'
5
5
  import styles from '../TitleBlock.module.scss'
6
6
  import { TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID } from '../constants'
7
7
  import { type SecondaryActionsProps, type TitleBlockMenuItemProps } from '../types'
8
+ import { convertSecondaryActionsToMenuItems } from '../utils'
8
9
  import { TitleBlockMenuItem } from './TitleBlockMenuItem'
9
10
  import { Toolbar } from './Toolbar'
10
11
 
@@ -18,25 +19,67 @@ const renderSecondaryOverflowMenu = (
18
19
  secondaryOverflowMenuItems?: TitleBlockMenuItemProps[],
19
20
  reversed?: boolean,
20
21
  ): JSX.Element | undefined => {
21
- if (!secondaryOverflowMenuItems) return undefined
22
+ if (!secondaryOverflowMenuItems || secondaryOverflowMenuItems.length === 0) return undefined
22
23
  return (
23
- <Menu
24
- align="right"
25
- button={
26
- <IconButton
27
- label="Open secondary menu"
28
- reversed={reversed}
29
- icon={<Icon name="more_horiz" isPresentational />}
30
- id={TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}
31
- />
32
- }
33
- >
34
- <MenuList>
35
- {secondaryOverflowMenuItems.map((menuItem, i) => (
36
- <TitleBlockMenuItem key={i} {...menuItem} />
37
- ))}
38
- </MenuList>
39
- </Menu>
24
+ <div className={styles.secondaryOverflowMenu}>
25
+ <Menu
26
+ align="right"
27
+ button={
28
+ <Button
29
+ secondary
30
+ reversed={reversed}
31
+ label="Menu"
32
+ data-automation-id={TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}
33
+ data-testid={TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}
34
+ icon={<Icon name="keyboard_arrow_down" isPresentational />}
35
+ iconPosition={'end'}
36
+ />
37
+ }
38
+ >
39
+ <MenuList>
40
+ {secondaryOverflowMenuItems.map((menuItem, i) => (
41
+ <TitleBlockMenuItem key={i} {...menuItem} />
42
+ ))}
43
+ </MenuList>
44
+ </Menu>
45
+ </div>
46
+ )
47
+ }
48
+
49
+ // New: combined overflow menu (secondary actions converted + overflow menu items)
50
+ const renderCombinedSecondaryOverflowMenu = (
51
+ secondaryActions?: SecondaryActionsProps,
52
+ secondaryOverflowMenuItems?: TitleBlockMenuItemProps[],
53
+ reversed?: boolean,
54
+ ): JSX.Element | undefined => {
55
+ const secondaryConverted = secondaryActions
56
+ ? convertSecondaryActionsToMenuItems(secondaryActions)
57
+ : []
58
+ const overflowItems = secondaryOverflowMenuItems ?? []
59
+
60
+ if (secondaryConverted.length === 0 && overflowItems.length === 0) return undefined
61
+ const combined = [...secondaryConverted, ...overflowItems]
62
+
63
+ return (
64
+ <div className={styles.secondaryOverflowCombinedMenu}>
65
+ <Menu
66
+ align="right"
67
+ button={
68
+ <IconButton
69
+ label="Open combined secondary + overflow menu"
70
+ reversed={reversed}
71
+ icon={<Icon name="more_horiz" isPresentational />}
72
+ id={`${TITLE_BLOCK_ZEN_SECONDARY_MENU_HTML_ID}__combined`}
73
+ />
74
+ }
75
+ >
76
+ <MenuList>
77
+ {combined.map((menuItem, i) => (
78
+ <TitleBlockMenuItem key={i} {...menuItem} />
79
+ ))}
80
+ </MenuList>
81
+ </Menu>
82
+ </div>
40
83
  )
41
84
  }
42
85
 
@@ -55,26 +98,28 @@ export const SecondaryActions = ({
55
98
  ? secondaryActions.map((action, i) => {
56
99
  if ('menuItems' in action) {
57
100
  return {
58
- key: `${i}`, // We shouldn't use an index here, see note above
101
+ key: `titleblock_secondary_action_${i}`,
59
102
  node: (
60
- <Menu
61
- align="right"
62
- button={
63
- <Button
64
- secondary
65
- label={action.label}
66
- reversed={reversed}
67
- icon={<Icon name="keyboard_arrow_down" isPresentational />}
68
- iconPosition="end"
69
- />
70
- }
71
- >
72
- <MenuList>
73
- {action.menuItems.map((menuItem, i2) => (
74
- <TitleBlockMenuItem key={i2} {...menuItem} />
75
- ))}
76
- </MenuList>
77
- </Menu>
103
+ <div className={styles.secondaryButtonContainer}>
104
+ <Menu
105
+ align="right"
106
+ button={
107
+ <Button
108
+ secondary
109
+ label={action.label}
110
+ reversed={reversed}
111
+ icon={<Icon name="keyboard_arrow_down" isPresentational />}
112
+ iconPosition="end"
113
+ />
114
+ }
115
+ >
116
+ <MenuList>
117
+ {action.menuItems.map((menuItem, i2) => (
118
+ <TitleBlockMenuItem key={i2} {...menuItem} />
119
+ ))}
120
+ </MenuList>
121
+ </Menu>
122
+ </div>
78
123
  ),
79
124
  }
80
125
  } else {
@@ -86,15 +131,17 @@ export const SecondaryActions = ({
86
131
  )
87
132
  }
88
133
  return {
89
- key: `${i}`, // We shouldn't use an index here, see note above
134
+ key: `${i}`,
90
135
  node: (
91
- <Button
92
- secondary
93
- reversed={reversed}
94
- {...action}
95
- data-automation-id="title-block-secondary-actions-button"
96
- data-testid="title-block-secondary-actions-button"
97
- />
136
+ <div className={styles.secondaryButtonContainer}>
137
+ <Button
138
+ secondary
139
+ reversed={reversed}
140
+ {...action}
141
+ data-automation-id="title-block-secondary-actions-button"
142
+ data-testid="title-block-secondary-actions-button"
143
+ />
144
+ </div>
98
145
  ),
99
146
  }
100
147
  }
@@ -102,6 +149,11 @@ export const SecondaryActions = ({
102
149
  : []
103
150
 
104
151
  const overflowMenu = renderSecondaryOverflowMenu(secondaryOverflowMenuItems, reversed)
152
+ const combinedOverflowMenu = renderCombinedSecondaryOverflowMenu(
153
+ secondaryActions,
154
+ secondaryOverflowMenuItems,
155
+ reversed,
156
+ )
105
157
 
106
158
  const toolbarItems = [
107
159
  ...secondaryActionsAsToolbarItems,
@@ -113,6 +165,14 @@ export const SecondaryActions = ({
113
165
  },
114
166
  ]
115
167
  : []),
168
+ ...(combinedOverflowMenu
169
+ ? [
170
+ {
171
+ key: 'combinedOverflowMenu',
172
+ node: combinedOverflowMenu,
173
+ },
174
+ ]
175
+ : []),
116
176
  ]
117
177
 
118
178
  return (
@@ -21,6 +21,7 @@ export const Toolbar = ({ items, noGap = false, automationId }: ToolbarProps): J
21
21
  if (!items || items?.length === 0) {
22
22
  return <></>
23
23
  }
24
+
24
25
  return (
25
26
  <div className={styles.toolbar} data-automation-id={automationId} data-testid={automationId}>
26
27
  {items.map((item) => (