@servicetitan/navigation 8.2.1 → 9.0.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 (61) hide show
  1. package/dist/components/counter-tag.d.ts +2 -4
  2. package/dist/components/counter-tag.d.ts.map +1 -1
  3. package/dist/components/counter-tag.js +3 -4
  4. package/dist/components/counter-tag.js.map +1 -1
  5. package/dist/components/header-navigation/header-navigation-content.d.ts +2 -2
  6. package/dist/components/header-navigation/header-navigation-content.d.ts.map +1 -1
  7. package/dist/components/header-navigation/header-navigation-content.js +4 -4
  8. package/dist/components/header-navigation/header-navigation-content.js.map +1 -1
  9. package/dist/components/header-navigation/header-navigation-extra.stories.js +3 -3
  10. package/dist/components/header-navigation/header-navigation-extra.stories.js.map +1 -1
  11. package/dist/components/header-navigation/header-navigation-links.js +4 -4
  12. package/dist/components/header-navigation/header-navigation-links.js.map +1 -1
  13. package/dist/components/header-navigation/header-navigation-stacked.stories.js +1 -1
  14. package/dist/components/header-navigation/header-navigation-stacked.stories.js.map +1 -1
  15. package/dist/components/header-navigation/header-navigation.module.less +2 -1
  16. package/dist/components/header-navigation/header-navigation.stories.js +1 -1
  17. package/dist/components/header-navigation/header-navigation.stories.js.map +1 -1
  18. package/dist/components/layout.stories.js +2 -2
  19. package/dist/components/layout.stories.js.map +1 -1
  20. package/dist/components/left-navigation/header-navigation-tiny-links.d.ts +1 -1
  21. package/dist/components/left-navigation/header-navigation-tiny-links.d.ts.map +1 -1
  22. package/dist/components/left-navigation/header-navigation-tiny-links.js +6 -6
  23. package/dist/components/left-navigation/header-navigation-tiny-links.js.map +1 -1
  24. package/dist/components/left-navigation/header-navigation-tiny.stories.js +2 -2
  25. package/dist/components/left-navigation/header-navigation-tiny.stories.js.map +1 -1
  26. package/dist/components/left-navigation/side-navigation.d.ts +8 -4
  27. package/dist/components/left-navigation/side-navigation.d.ts.map +1 -1
  28. package/dist/components/left-navigation/side-navigation.js +30 -20
  29. package/dist/components/left-navigation/side-navigation.js.map +1 -1
  30. package/dist/components/left-navigation/side-navigation.module.less +20 -12
  31. package/dist/components/left-navigation/side-navigation.stories.d.ts.map +1 -1
  32. package/dist/components/left-navigation/side-navigation.stories.js +7 -7
  33. package/dist/components/left-navigation/side-navigation.stories.js.map +1 -1
  34. package/dist/components/profile-dropdown/profile-dropdown.d.ts +1 -1
  35. package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
  36. package/dist/components/profile-dropdown/profile-dropdown.js +3 -3
  37. package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
  38. package/dist/test/data.d.ts +1 -0
  39. package/dist/test/data.d.ts.map +1 -1
  40. package/dist/test/data.js +10 -8
  41. package/dist/test/data.js.map +1 -1
  42. package/dist/utils/navigation.d.ts +9 -5
  43. package/dist/utils/navigation.d.ts.map +1 -1
  44. package/package.json +5 -5
  45. package/src/components/counter-tag.tsx +12 -9
  46. package/src/components/header-navigation/header-navigation-content.tsx +7 -7
  47. package/src/components/header-navigation/header-navigation-extra.stories.tsx +3 -3
  48. package/src/components/header-navigation/header-navigation-links.tsx +4 -4
  49. package/src/components/header-navigation/header-navigation-stacked.stories.tsx +2 -2
  50. package/src/components/header-navigation/header-navigation.module.less +2 -1
  51. package/src/components/header-navigation/header-navigation.stories.tsx +2 -2
  52. package/src/components/layout.stories.tsx +2 -2
  53. package/src/components/left-navigation/header-navigation-tiny-links.tsx +8 -8
  54. package/src/components/left-navigation/header-navigation-tiny.stories.tsx +2 -2
  55. package/src/components/left-navigation/side-navigation.module.less +20 -12
  56. package/src/components/left-navigation/side-navigation.module.less.d.ts +1 -0
  57. package/src/components/left-navigation/side-navigation.stories.tsx +14 -8
  58. package/src/components/left-navigation/side-navigation.tsx +56 -43
  59. package/src/components/profile-dropdown/profile-dropdown.tsx +6 -9
  60. package/src/test/data.tsx +24 -12
  61. package/src/utils/navigation.ts +10 -5
@@ -12,7 +12,6 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
12
12
  to,
13
13
  hint,
14
14
  tooltip,
15
- counter,
16
15
  className,
17
16
  icon,
18
17
  iconActive,
@@ -22,6 +21,7 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
22
21
  isActive,
23
22
  label,
24
23
  labelClassName,
24
+ tag,
25
25
  target,
26
26
  ...rest
27
27
  }) => {
@@ -43,7 +43,7 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
43
43
  target={target}
44
44
  >
45
45
  <HeaderNavigationItemContent
46
- counter={counter}
46
+ tag={tag}
47
47
  iconComponent={iconComponent}
48
48
  iconClassName={iconClassName}
49
49
  iconName={iconName}
@@ -59,7 +59,6 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
59
59
  export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
60
60
  id,
61
61
  className,
62
- counter,
63
62
  iconClassName,
64
63
  iconComponent,
65
64
  iconName,
@@ -67,6 +66,7 @@ export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
67
66
  label,
68
67
  labelClassName,
69
68
  hint,
69
+ tag,
70
70
  tooltip,
71
71
  ...rest
72
72
  }) => {
@@ -87,7 +87,7 @@ export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
87
87
  )}
88
88
  >
89
89
  <HeaderNavigationItemContent
90
- counter={counter}
90
+ tag={tag}
91
91
  iconComponent={iconComponent}
92
92
  iconClassName={iconClassName}
93
93
  iconName={iconName}
@@ -96,7 +96,7 @@ export const WithAllMonolithData = () => (
96
96
  <HeaderNavigationTrigger
97
97
  id="dialpad"
98
98
  iconName="phone"
99
- counter={2}
99
+ tag={2}
100
100
  icon={SvgIcon}
101
101
  iconActive={SvgIcon}
102
102
  />
@@ -122,7 +122,7 @@ export const WithAllMonolithData = () => (
122
122
  />
123
123
 
124
124
  <ProfileDropdown>
125
- <ProfileDropdown.Link id="tasks" to="https://googgle.com" counter={10}>
125
+ <ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
126
126
  Task Management
127
127
  </ProfileDropdown.Link>
128
128
  <ProfileDropdown.Divider />
@@ -219,7 +219,8 @@
219
219
  .navigation-item-counter {
220
220
  color: @color-white;
221
221
  font-weight: @font-weight-semibold;
222
- min-width: 12px;
222
+ min-width: 12px !important;
223
+ min-height: 12px !important;
223
224
  }
224
225
 
225
226
  &:not(.navigation-item-overflow) .navigation-item-counter {
@@ -108,7 +108,7 @@ export const WithAllMonolithData = () => (
108
108
  <HeaderNavigationTrigger
109
109
  id="dialpad"
110
110
  iconName="phone"
111
- counter={2}
111
+ tag={2}
112
112
  icon={SvgIcon}
113
113
  iconActive={SvgIcon}
114
114
  />
@@ -145,7 +145,7 @@ export const WithAllMonolithData = () => (
145
145
  />
146
146
 
147
147
  <ProfileDropdown>
148
- <ProfileDropdown.Link id="tasks" to="https://googgle.com" counter={10}>
148
+ <ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
149
149
  Task Management
150
150
  </ProfileDropdown.Link>
151
151
  <ProfileDropdown.Divider />
@@ -1,5 +1,5 @@
1
1
  import { Page, Sidebar } from '@servicetitan/design-system';
2
- import { LocationInfo, withAnvil, withMemoryRouter } from '../test/data';
2
+ import { LocationInfo, withAnvil, withDefaultRedirects, withMemoryRouter } from '../test/data';
3
3
  import {
4
4
  WithAllMonolithData,
5
5
  WithAllMonolithDataCommercial,
@@ -13,7 +13,7 @@ import {
13
13
  export default {
14
14
  title: 'Navigation/Layout',
15
15
  parameters: {},
16
- decorators: [withMemoryRouter, withAnvil],
16
+ decorators: [withDefaultRedirects, withMemoryRouter, withAnvil],
17
17
  };
18
18
 
19
19
  export const LeftNavLayout = () => {
@@ -9,13 +9,13 @@ import { withTooltip } from './with-tooltip';
9
9
 
10
10
  /** Content for navigation items */
11
11
  export const HeaderNavigationItemContent: FC<{
12
- counter?: CounterTagPropsType;
12
+ tag?: CounterTagPropsType;
13
13
  counterClassName?: string;
14
14
  icon: IconProps['svg'] | undefined;
15
15
  iconActive: IconProps['svg'] | undefined;
16
16
  label?: string;
17
17
  labelClassName?: string;
18
- }> = ({ counter, counterClassName, icon, iconActive, label, labelClassName }) => {
18
+ }> = ({ counterClassName, icon, iconActive, label, labelClassName, tag }) => {
19
19
  return (
20
20
  <Fragment>
21
21
  {!!icon && <Icon svg={icon} className={Styles.navigationIcon} />}
@@ -32,9 +32,9 @@ export const HeaderNavigationItemContent: FC<{
32
32
  </span>
33
33
  )}
34
34
 
35
- {!!counter && (
35
+ {!!tag && (
36
36
  <CounterTag
37
- data={counter}
37
+ data={tag}
38
38
  className={classNames(Styles.navigationItemCounter, counterClassName)}
39
39
  longClassName={Styles.navigationItemCounterLong}
40
40
  />
@@ -49,13 +49,13 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
49
49
  to,
50
50
  hint,
51
51
  tooltip,
52
- counter,
53
52
  className,
54
53
  icon,
55
54
  iconActive,
56
55
  isActive,
57
56
  label,
58
57
  labelClassName,
58
+ tag,
59
59
  target,
60
60
  ...rest
61
61
  }) => {
@@ -78,7 +78,7 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
78
78
  target={target}
79
79
  >
80
80
  <HeaderNavigationItemContent
81
- counter={counter}
81
+ tag={tag}
82
82
  icon={icon}
83
83
  iconActive={iconActive}
84
84
  label={label}
@@ -93,13 +93,13 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
93
93
  export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
94
94
  id,
95
95
  className,
96
- counter,
97
96
  icon,
98
97
  iconActive,
99
98
  isActive,
100
99
  hint,
101
100
  label,
102
101
  labelClassName,
102
+ tag,
103
103
  tooltip,
104
104
  title,
105
105
  titleClassName,
@@ -122,7 +122,7 @@ export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
122
122
  )}
123
123
  >
124
124
  <HeaderNavigationItemContent
125
- counter={counter}
125
+ tag={tag}
126
126
  icon={icon}
127
127
  iconActive={iconActive}
128
128
  label={label}
@@ -86,7 +86,7 @@ export const WithAllMonolithData = () => (
86
86
  />
87
87
 
88
88
  <ProfileDropdown>
89
- <ProfileDropdown.Link id="tasks" to="https://googgle.com" counter={10}>
89
+ <ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
90
90
  Task Management
91
91
  </ProfileDropdown.Link>
92
92
  <ProfileDropdown.Divider />
@@ -152,7 +152,7 @@ export const WithAllMonolithDataCommercial = () => (
152
152
  />
153
153
 
154
154
  <ProfileDropdown>
155
- <ProfileDropdown.Link id="tasks" to="https://googgle.com" counter={10}>
155
+ <ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
156
156
  Task Management
157
157
  </ProfileDropdown.Link>
158
158
  <ProfileDropdown.Divider />
@@ -73,7 +73,6 @@
73
73
 
74
74
  .navigation-item {
75
75
  flex-direction: row;
76
- margin-bottom: @spacing-half;
77
76
  margin-left: @spacing-1;
78
77
  margin-right: @spacing-1;
79
78
 
@@ -85,9 +84,13 @@
85
84
  .navigation-item-text {
86
85
  font-family: @base-font-family;
87
86
  font-size: @typescale-3;
88
- padding-left: @spacing-2;
87
+ padding-left: @spacing-1;
89
88
  flex: 1;
90
89
  }
90
+
91
+ .navigation-item-counter {
92
+ margin-right: @spacing-1;
93
+ }
91
94
  }
92
95
 
93
96
  .options-item {
@@ -189,6 +192,8 @@
189
192
  .navigation-item-counter {
190
193
  color: @color-white;
191
194
  font-weight: @font-weight-semibold;
195
+ min-width: 12px !important;
196
+ min-height: 12px !important;
192
197
  }
193
198
 
194
199
  .navigation-item-group-toggle[data-anv][data-anv] {
@@ -215,16 +220,6 @@
215
220
  margin-bottom: @spacing-1;
216
221
  position: relative;
217
222
 
218
- &:before {
219
- content: '';
220
- position: absolute;
221
- border-left: 1px solid @color-neutral-100;
222
- width: 1px;
223
- top: @spacing-2;
224
- bottom: @spacing-1;
225
- left: 0;
226
- }
227
-
228
223
  .submenu-group-header[data-anv][data-anv] {
229
224
  padding-top: @spacing-2;
230
225
  padding-bottom: @spacing-half;
@@ -305,12 +300,25 @@
305
300
  color: @text-color;
306
301
  font-size: @typescale-2;
307
302
  border-radius: @border-radius-2;
303
+ display: flex;
304
+ flex-direction: row;
305
+ justify-content: space-between;
306
+ align-items: center;
308
307
  }
309
308
 
310
309
  .submenu-link-active {
311
310
  color: @text-color-active;
312
311
  }
313
312
 
313
+ .submenu-link-counter {
314
+ min-height: 19px !important;
315
+ min-width: 19px !important;
316
+
317
+ > span:first-child {
318
+ color: @color-black;
319
+ }
320
+ }
321
+
314
322
  .submenu-link:hover:not(.submenu-link-active) {
315
323
  background-color: @bg-color-hover;
316
324
  }
@@ -25,5 +25,6 @@ export const submenu: string;
25
25
  export const submenuGroupHeader: string;
26
26
  export const submenuLink: string;
27
27
  export const submenuLinkActive: string;
28
+ export const submenuLinkCounter: string;
28
29
  export const submenuPopover: string;
29
30
 
@@ -1,14 +1,20 @@
1
- import { Page, Sidebar } from '@servicetitan/design-system';
1
+ import { Page } from '@servicetitan/design-system';
2
2
  import { ComponentType, useState } from 'react';
3
- import { LocationInfo, items, withAnvil, withMemoryRouter } from '../../test/data';
4
- import { SideNavigation } from './';
3
+ import {
4
+ LocationInfo,
5
+ items,
6
+ withAnvil,
7
+ withDefaultRedirects,
8
+ withMemoryRouter,
9
+ } from '../../test/data';
10
+ import { SideNavigation, SideNavigationExpandedState } from './';
5
11
 
6
12
  const layout = (Story: ComponentType) => {
7
13
  return (
8
14
  <div className="d-f border" style={{ height: '800px' }}>
9
15
  <Story />
10
16
  <div className="flex-grow-1 flex-basis-0">
11
- <Page sidebar={<Sidebar localStorageKey="undefined">qq</Sidebar>}>
17
+ <Page>
12
18
  <LocationInfo />
13
19
  </Page>
14
20
  </div>
@@ -18,12 +24,12 @@ const layout = (Story: ComponentType) => {
18
24
  export default {
19
25
  title: 'Navigation/SideNavigation',
20
26
  component: SideNavigation,
21
- decorators: [layout, withMemoryRouter, withAnvil],
27
+ decorators: [layout, withDefaultRedirects, withMemoryRouter, withAnvil],
22
28
  parameters: {},
23
29
  };
24
30
 
25
31
  export const DefaultSideNavigation = () => {
26
- const [expanded, setExpanded] = useState(false);
32
+ const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
27
33
  return (
28
34
  <SideNavigation
29
35
  expanded={expanded}
@@ -50,7 +56,7 @@ export const DefaultSideNavigation = () => {
50
56
  };
51
57
 
52
58
  export const SideNavigationLinksOnly = () => {
53
- const [expanded, setExpanded] = useState(false);
59
+ const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
54
60
  return (
55
61
  <SideNavigation
56
62
  expanded={expanded}
@@ -76,7 +82,7 @@ export const SideNavigationLinksOnly = () => {
76
82
  };
77
83
 
78
84
  export const SideNavigationWithSubmenu = () => {
79
- const [expanded, setExpanded] = useState(false);
85
+ const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
80
86
  return (
81
87
  <SideNavigation
82
88
  expanded={expanded}
@@ -14,12 +14,11 @@ import {
14
14
  ReactElement,
15
15
  useCallback,
16
16
  useContext,
17
- useState,
18
17
  } from 'react';
19
18
  import {
20
19
  HeaderNavigationItemData,
21
- HeaderNavigationItemLinkProps,
22
20
  HeaderNavigationItemSubmenu,
21
+ HeaderNavigationItemSubmenuLink,
23
22
  NavLinkComponentProps,
24
23
  } from '../../utils/navigation';
25
24
  import { NavigationComponentContext } from '../../utils/navigation-context';
@@ -27,6 +26,11 @@ import { CounterTag } from '../counter-tag';
27
26
  import * as Styles from './side-navigation.module.less';
28
27
  import { withTooltip } from './with-tooltip';
29
28
 
29
+ export interface SideNavigationExpandedState {
30
+ bar: boolean;
31
+ submenus?: string[];
32
+ }
33
+
30
34
  export interface SideNavigationProps {
31
35
  /** container class name */
32
36
  className?: string;
@@ -37,9 +41,9 @@ export interface SideNavigationProps {
37
41
  /** top navigation items */
38
42
  itemsTop?: HeaderNavigationItemData[];
39
43
  /** is menu expanded */
40
- expanded?: boolean;
44
+ expanded?: SideNavigationExpandedState;
41
45
  /** expand change handler */
42
- onExpandedChange(expanded: boolean): void;
46
+ onExpandedChange?(expanded: SideNavigationExpandedState): void;
43
47
  }
44
48
 
45
49
  export const SideNavigation: FC<SideNavigationProps> = ({
@@ -55,7 +59,7 @@ export const SideNavigation: FC<SideNavigationProps> = ({
55
59
  <div
56
60
  className={classNames(
57
61
  Styles.sideNav,
58
- expanded ? Styles.sideNavExpanded : Styles.sideNavSlim,
62
+ expanded?.bar ? Styles.sideNavExpanded : Styles.sideNavSlim,
59
63
  className
60
64
  )}
61
65
  id={id}
@@ -82,6 +86,7 @@ export const SideNavigation: FC<SideNavigationProps> = ({
82
86
  <SideNavigationGroupItem
83
87
  key={item.id}
84
88
  expanded={expanded}
89
+ onExpandedChange={onExpandedChange}
85
90
  navigationComponent={NavigationComponent}
86
91
  {...item}
87
92
  />
@@ -111,18 +116,14 @@ interface NavigationComponentProps {
111
116
  }
112
117
 
113
118
  interface SideNavigationItemProps extends HeaderNavigationItemData, NavigationComponentProps {
114
- expanded?: boolean;
115
- submenuExpanded?: boolean;
119
+ expanded?: SideNavigationExpandedState;
116
120
  }
117
121
 
118
122
  /** Side Navigation menu item */
119
123
  const SideNavigationItem: FC<SideNavigationItemProps> = ({
120
124
  id,
121
- submenuExpanded,
122
125
  to,
123
126
  title,
124
- hint,
125
- counter,
126
127
  className,
127
128
  iconClassName,
128
129
  iconComponent: IconComponent,
@@ -130,10 +131,12 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
130
131
  iconActive,
131
132
  isActive,
132
133
  navigationComponent: NavigationComponent,
134
+ tag,
133
135
  expanded,
136
+ submenu,
134
137
  }) => {
135
138
  const iconSwitch = !!icon && !!iconActive && !IconComponent;
136
- const hasSubmenu = submenuExpanded === true || submenuExpanded === false;
139
+ const hasSubmenu = !!submenu;
137
140
 
138
141
  return (
139
142
  <NavigationComponent
@@ -141,7 +144,6 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
141
144
  data-pendo={`navigation-item-${id}`}
142
145
  key={id}
143
146
  to={to}
144
- title={hint}
145
147
  className={classNames(Styles.navigationItem, className, {
146
148
  [Styles.navigationItemActive]: isActive === true,
147
149
  [Styles.navigationItemIconSwitch]: iconSwitch,
@@ -179,19 +181,17 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
179
181
  </Fragment>
180
182
  )}
181
183
 
182
- {!!expanded && <div className={Styles.navigationItemText}>{title}</div>}
183
- {!!counter && (
184
- <CounterTag data={counter} className={Styles.navigationItemCounter} />
185
- )}
186
- {hasSubmenu && !!expanded && (
184
+ {!!expanded?.bar && <div className={Styles.navigationItemText}>{title}</div>}
185
+ {!!tag && <CounterTag data={tag} className={Styles.navigationItemCounter} />}
186
+ {hasSubmenu && !!expanded?.bar && (
187
187
  <Icon
188
- svg={submenuExpanded ? SvgGroupCollapse : SvgGroupExpand}
188
+ svg={expanded?.submenus?.includes(id) ? SvgGroupCollapse : SvgGroupExpand}
189
189
  className={Styles.navigationItemGroupToggle}
190
190
  />
191
191
  )}
192
192
  </div>
193
193
 
194
- {!expanded && (
194
+ {!expanded?.bar && (
195
195
  <div
196
196
  className={classNames(Styles.navigationItemText, {
197
197
  [Styles.navigationItemTextSmall]: title.length >= 10,
@@ -207,26 +207,38 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
207
207
  const submenuPopoverStyles = { '--background-color-strong': '#24323C' } as CSSProperties;
208
208
 
209
209
  /** Side Navigation menu item */
210
- const SideNavigationGroupItem: FC<SideNavigationItemProps> = ({ ...props }) => {
211
- const [submenuExpanded, setSubmenuExpanded] = useState(false);
210
+ const SideNavigationGroupItem: FC<
211
+ SideNavigationItemProps & {
212
+ onExpandedChange: undefined | ((expanded: SideNavigationExpandedState) => void);
213
+ }
214
+ > = ({ onExpandedChange, ...props }) => {
212
215
  const triggerClick = useCallback(
213
216
  (e: MouseEvent<HTMLDivElement>) => {
214
217
  e.stopPropagation();
215
218
  e.preventDefault();
216
219
 
217
- if (props.expanded) {
218
- setSubmenuExpanded(exp => !exp);
219
- }
220
+ const isSubmenuExpanded = props.expanded?.submenus?.includes(props.id);
221
+ onExpandedChange?.({
222
+ bar: !!props.expanded?.bar,
223
+ submenus: [
224
+ ...(props.expanded?.submenus?.filter(id => id !== props.id) ?? []),
225
+ ...(isSubmenuExpanded ? [] : [props.id]),
226
+ ],
227
+ });
220
228
  },
221
- [props.expanded]
229
+ [props.id, props.expanded, onExpandedChange]
222
230
  );
223
231
 
224
- return props.expanded ? (
232
+ const tag = props.submenu?.groups.some(group => group.links.some(link => !!link.tag))
233
+ ? true
234
+ : props.tag;
235
+
236
+ return props.expanded?.bar ? (
225
237
  <Fragment>
226
238
  <div onClickCapture={triggerClick}>
227
- <SideNavigationItem {...props} submenuExpanded={submenuExpanded} />
239
+ <SideNavigationItem {...props} tag={tag} />
228
240
  </div>
229
- <Collapsible open={submenuExpanded} animate>
241
+ <Collapsible open={props.expanded?.submenus?.includes(props.id)} animate>
230
242
  <div className={Styles.submenu}>
231
243
  <SideNavigationGroupContent
232
244
  groups={props.submenu?.groups ?? []}
@@ -239,8 +251,8 @@ const SideNavigationGroupItem: FC<SideNavigationItemProps> = ({ ...props }) => {
239
251
  <Popover placement="right-start" openOnHover>
240
252
  <Popover.Trigger>
241
253
  {(triggerProps: PopoverTriggerProps) => (
242
- <div {...triggerProps} onClickCapture={triggerClick}>
243
- <SideNavigationItem {...props} />
254
+ <div {...triggerProps}>
255
+ <SideNavigationItem {...props} tag={tag} />
244
256
  </div>
245
257
  )}
246
258
  </Popover.Trigger>
@@ -264,17 +276,14 @@ const SideNavigationGroupContent: FC<HeaderNavigationItemSubmenu & NavigationCom
264
276
  }) => {
265
277
  return (
266
278
  <Fragment>
267
- {groups.reduce((out, group) => {
279
+ {groups.reduce((out, group, index) => {
268
280
  if (!group.links.length) {
269
281
  return out;
270
282
  }
271
283
 
284
+ const key = `:group:${index}:title`;
272
285
  out.push(
273
- <Text
274
- key=":group:title"
275
- variant="eyebrow"
276
- className={Styles.submenuGroupHeader}
277
- >
286
+ <Text key={key} variant="eyebrow" className={Styles.submenuGroupHeader}>
278
287
  {group.title}
279
288
  </Text>
280
289
  );
@@ -293,8 +302,9 @@ const SideNavigationGroupContent: FC<HeaderNavigationItemSubmenu & NavigationCom
293
302
  </Fragment>
294
303
  );
295
304
  };
296
- const SideNavigationGroupLink: FC<HeaderNavigationItemLinkProps & NavigationComponentProps> = ({
305
+ const SideNavigationGroupLink: FC<HeaderNavigationItemSubmenuLink & NavigationComponentProps> = ({
297
306
  id,
307
+ tag,
298
308
  title,
299
309
  to,
300
310
  isActive,
@@ -312,29 +322,32 @@ const SideNavigationGroupLink: FC<HeaderNavigationItemLinkProps & NavigationComp
312
322
  isActive={typeof isActive === 'function' ? isActive : undefined}
313
323
  activeClassName={Styles.submenuLinkActive}
314
324
  >
315
- {title}
325
+ <span>{title}</span>
326
+ {!!tag && <CounterTag data={tag} className={Styles.submenuLinkCounter} />}
316
327
  </NavigationComponent>
317
328
  );
318
329
  };
319
330
 
320
331
  /** Side Navigation options toggle */
321
332
  export const SideNavigationOptionsToggle: FC<{
322
- expanded?: boolean;
323
- onExpandedChange(expanded: boolean): void;
333
+ expanded?: SideNavigationExpandedState;
334
+ onExpandedChange?(expanded: SideNavigationExpandedState): void;
324
335
  }> = ({ expanded, onExpandedChange }) =>
325
336
  withTooltip(
326
337
  <div
327
338
  data-cy="navigation-left-options"
328
339
  data-pendo="navigation-left-options"
329
340
  className={classNames(Styles.optionsItem)}
330
- onClick={() => onExpandedChange(!expanded)}
341
+ onClick={() =>
342
+ onExpandedChange?.({ bar: !expanded?.bar, submenus: expanded?.submenus })
343
+ }
331
344
  >
332
345
  <div className={Styles.optionsIconWrapper}>
333
346
  <Icon className={Styles.optionsIcon} svg={expanded ? SvgCollapse : SvgExpand} />
334
347
  </div>
335
348
 
336
- {!!expanded && <span className={Styles.optionsItemText}>Collapse Menu</span>}
349
+ {!!expanded?.bar && <span className={Styles.optionsItemText}>Collapse Menu</span>}
337
350
  </div>,
338
- expanded ? undefined : 'Expand Menu',
351
+ expanded?.bar ? undefined : 'Expand Menu',
339
352
  'right'
340
353
  );
@@ -192,7 +192,7 @@ export interface ProfileDropdownLinkPropsStrict {
192
192
  external?: boolean;
193
193
  target?: HTMLAttributeAnchorTarget;
194
194
  to?: string;
195
- counter?: CounterTagPropsType;
195
+ tag?: CounterTagPropsType;
196
196
  onClick?: () => void;
197
197
  }
198
198
 
@@ -203,9 +203,9 @@ export interface ProfileDropdownLinkProps extends ProfileDropdownLinkPropsStrict
203
203
  export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
204
204
  children,
205
205
  className,
206
- counter,
207
206
  external,
208
207
  id,
208
+ tag,
209
209
  target,
210
210
  to,
211
211
  onClick,
@@ -220,10 +220,7 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
220
220
 
221
221
  const isExternalLink = external ?? to?.startsWith('http');
222
222
 
223
- const counterElement = useMemo(
224
- () => <CounterTag data={counter} className={Styles.counter} />,
225
- [counter]
226
- );
223
+ const tagElement = useMemo(() => <CounterTag data={tag} className={Styles.counter} />, [tag]);
227
224
 
228
225
  return isExternalLink ? (
229
226
  <a
@@ -235,7 +232,7 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
235
232
  {...rest}
236
233
  >
237
234
  {children}
238
- {counterElement}
235
+ {tagElement}
239
236
  </a>
240
237
  ) : to ? (
241
238
  <NavigationComponent
@@ -247,7 +244,7 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
247
244
  {...rest}
248
245
  >
249
246
  {children}
250
- {counterElement}
247
+ {tagElement}
251
248
  </NavigationComponent>
252
249
  ) : (
253
250
  <a
@@ -258,7 +255,7 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
258
255
  {...rest}
259
256
  >
260
257
  {children}
261
- {counterElement}
258
+ {tagElement}
262
259
  </a>
263
260
  );
264
261
  };