@servicetitan/navigation 11.0.0-canary.237.0ce6038.0 → 11.0.0-canary.237.1127df0.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 (91) hide show
  1. package/dist/components/badge-tag.d.ts +1 -1
  2. package/dist/components/badge-tag.d.ts.map +1 -1
  3. package/dist/components/header-navigation/header-navigation-stacked.stories.js +1 -1
  4. package/dist/components/header-navigation/header-navigation-stacked.stories.js.map +1 -1
  5. package/dist/components/header-navigation/header-navigation.stories.js +1 -1
  6. package/dist/components/header-navigation/header-navigation.stories.js.map +1 -1
  7. package/dist/components/left-navigation/header-navigation-tiny.stories.js +2 -2
  8. package/dist/components/left-navigation/header-navigation-tiny.stories.js.map +1 -1
  9. package/dist/components/logo/logo-titan-text.d.ts +1 -1
  10. package/dist/components/logo/logo-titan-text.d.ts.map +1 -1
  11. package/dist/components/profile-dropdown/profile-dropdown.d.ts +10 -7
  12. package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
  13. package/dist/components/profile-dropdown/profile-dropdown.js +2 -2
  14. package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
  15. package/dist/components/profile-dropdown/profile-dropdown.module.less +2 -0
  16. package/dist/components/titan-layout/layout-context.js +1 -1
  17. package/dist/components/titan-layout/layout-context.js.map +1 -1
  18. package/dist/components/titan-layout/layout-header.d.ts +2 -0
  19. package/dist/components/titan-layout/layout-header.d.ts.map +1 -1
  20. package/dist/components/titan-layout/layout-header.js +3 -4
  21. package/dist/components/titan-layout/layout-header.js.map +1 -1
  22. package/dist/components/titan-layout/layout-header.module.less +60 -21
  23. package/dist/components/titan-layout/layout-logo.d.ts.map +1 -1
  24. package/dist/components/titan-layout/layout-logo.js +2 -1
  25. package/dist/components/titan-layout/layout-logo.js.map +1 -1
  26. package/dist/components/titan-layout/layout-profile.d.ts.map +1 -1
  27. package/dist/components/titan-layout/layout-profile.js +33 -9
  28. package/dist/components/titan-layout/layout-profile.js.map +1 -1
  29. package/dist/components/titan-layout/layout-profile.stories.d.ts.map +1 -1
  30. package/dist/components/titan-layout/layout-profile.stories.js +1 -1
  31. package/dist/components/titan-layout/layout-profile.stories.js.map +1 -1
  32. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts +2 -2
  33. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts.map +1 -1
  34. package/dist/components/titan-layout/layout-sidebar-links-internal.js +4 -4
  35. package/dist/components/titan-layout/layout-sidebar-links-internal.js.map +1 -1
  36. package/dist/components/titan-layout/layout-sidebar-links.d.ts.map +1 -1
  37. package/dist/components/titan-layout/layout-sidebar-links.js +12 -5
  38. package/dist/components/titan-layout/layout-sidebar-links.js.map +1 -1
  39. package/dist/components/titan-layout/layout-sidebar.d.ts +2 -0
  40. package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
  41. package/dist/components/titan-layout/layout-sidebar.js +6 -4
  42. package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
  43. package/dist/components/titan-layout/layout-sidebar.module.less +29 -6
  44. package/dist/components/titan-layout/notifications-context.d.ts +13 -0
  45. package/dist/components/titan-layout/notifications-context.d.ts.map +1 -0
  46. package/dist/components/titan-layout/notifications-context.js +23 -0
  47. package/dist/components/titan-layout/notifications-context.js.map +1 -0
  48. package/dist/components/titan-layout/titan-layout.d.ts +10 -6
  49. package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
  50. package/dist/components/titan-layout/titan-layout.js +104 -21
  51. package/dist/components/titan-layout/titan-layout.js.map +1 -1
  52. package/dist/components/titan-layout/titan-layout.module.less +50 -20
  53. package/dist/components/titan-layout/titan-layout.stories.d.ts +15 -11
  54. package/dist/components/titan-layout/titan-layout.stories.d.ts.map +1 -1
  55. package/dist/components/titan-layout/titan-layout.stories.js +35 -14
  56. package/dist/components/titan-layout/titan-layout.stories.js.map +1 -1
  57. package/dist/test/data.d.ts +4 -1
  58. package/dist/test/data.d.ts.map +1 -1
  59. package/dist/test/data.js +2 -3
  60. package/dist/test/data.js.map +1 -1
  61. package/dist/utils/use-breakpoint.d.ts +1 -0
  62. package/dist/utils/use-breakpoint.d.ts.map +1 -1
  63. package/dist/utils/use-breakpoint.js +3 -2
  64. package/dist/utils/use-breakpoint.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/components/badge-tag.tsx +1 -1
  67. package/src/components/header-navigation/header-navigation-stacked.stories.tsx +1 -1
  68. package/src/components/header-navigation/header-navigation.stories.tsx +1 -1
  69. package/src/components/left-navigation/header-navigation-tiny.stories.tsx +2 -2
  70. package/src/components/logo/logo-titan-text.tsx +1 -1
  71. package/src/components/profile-dropdown/profile-dropdown.module.less +2 -0
  72. package/src/components/profile-dropdown/profile-dropdown.tsx +13 -6
  73. package/src/components/titan-layout/layout-context.tsx +1 -1
  74. package/src/components/titan-layout/layout-header.module.less +60 -21
  75. package/src/components/titan-layout/layout-header.module.less.d.ts +2 -0
  76. package/src/components/titan-layout/layout-header.tsx +17 -6
  77. package/src/components/titan-layout/layout-logo.tsx +13 -6
  78. package/src/components/titan-layout/layout-profile.stories.tsx +10 -1
  79. package/src/components/titan-layout/layout-profile.tsx +81 -26
  80. package/src/components/titan-layout/layout-sidebar-links-internal.tsx +18 -5
  81. package/src/components/titan-layout/layout-sidebar-links.tsx +21 -5
  82. package/src/components/titan-layout/layout-sidebar.module.less +29 -6
  83. package/src/components/titan-layout/layout-sidebar.module.less.d.ts +1 -0
  84. package/src/components/titan-layout/layout-sidebar.tsx +14 -5
  85. package/src/components/titan-layout/notifications-context.tsx +44 -0
  86. package/src/components/titan-layout/titan-layout.module.less +50 -20
  87. package/src/components/titan-layout/titan-layout.module.less.d.ts +3 -0
  88. package/src/components/titan-layout/titan-layout.stories.tsx +166 -19
  89. package/src/components/titan-layout/titan-layout.tsx +265 -76
  90. package/src/test/data.tsx +2 -3
  91. package/src/utils/use-breakpoint.ts +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/navigation",
3
- "version": "11.0.0-canary.237.0ce6038.0",
3
+ "version": "11.0.0-canary.237.1127df0.0",
4
4
  "description": "Navigation components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,5 +42,5 @@
42
42
  "less": true,
43
43
  "webpack": false
44
44
  },
45
- "gitHead": "0ce60388dae320a497061d0f4703e731a1c7c279"
45
+ "gitHead": "1127df0cf95ea4e6e4b074bf79895bed386049c9"
46
46
  }
@@ -5,7 +5,7 @@ import { CounterTagData } from '../utils/counter-tag';
5
5
 
6
6
  export interface BadgeTagProps extends CounterTagData {
7
7
  [key: string]: any;
8
- className: string;
8
+ className?: string;
9
9
  }
10
10
 
11
11
  export const BadgeTag: FC<{
@@ -130,7 +130,7 @@ export const WithAllMonolithData = () => (
130
130
  Task Management
131
131
  </ProfileDropdown.Link>
132
132
  <ProfileDropdown.Divider />
133
- <ProfileDropdown.Link id="sign-out" to="https://googgle.com">
133
+ <ProfileDropdown.Link id="sign-out" to="https://googgle.com" text="Sign Out">
134
134
  Sign Out{' '}
135
135
  <span className="c-neutral-100 m-l-half t-truncate">James Bond</span>
136
136
  </ProfileDropdown.Link>
@@ -154,7 +154,7 @@ export const WithAllMonolithData = () => (
154
154
  Task Management
155
155
  </ProfileDropdown.Link>
156
156
  <ProfileDropdown.Divider />
157
- <ProfileDropdown.Link id="sign-out" to="https://googgle.com">
157
+ <ProfileDropdown.Link id="sign-out" to="https://googgle.com" text="Sign Out">
158
158
  Sign Out <span className="c-neutral-100 m-l-half t-truncate">James Bond</span>
159
159
  </ProfileDropdown.Link>
160
160
  <ProfileDropdown.Section id="userid" className="c-neutral-100 fs-1">
@@ -93,7 +93,7 @@ export const WithAllMonolithData = () => (
93
93
  Task Management
94
94
  </ProfileDropdown.Link>
95
95
  <ProfileDropdown.Divider />
96
- <ProfileDropdown.Link id="sign-out" to="https://googgle.com">
96
+ <ProfileDropdown.Link id="sign-out" to="https://googgle.com" text="Sign Out">
97
97
  Sign Out{' '}
98
98
  <span className="c-neutral-100 m-l-half t-truncate">James Bond</span>
99
99
  </ProfileDropdown.Link>
@@ -162,7 +162,7 @@ export const WithAllMonolithDataCommercial = () => (
162
162
  Task Management
163
163
  </ProfileDropdown.Link>
164
164
  <ProfileDropdown.Divider />
165
- <ProfileDropdown.Link id="sign-out" to="https://googgle.com">
165
+ <ProfileDropdown.Link id="sign-out" to="https://googgle.com" text="Sign Out">
166
166
  Sign Out{' '}
167
167
  <span className="c-neutral-100 m-l-half t-truncate">James Bond</span>
168
168
  </ProfileDropdown.Link>
@@ -3,7 +3,7 @@ import { CSSProperties, FC, ReactNode } from 'react';
3
3
  import { LogoTitanSvg } from './logo-titan';
4
4
  import * as Styles from './logo-titan-text.module.less';
5
5
 
6
- export type WrapperProps = FC<{ className: string; children: ReactNode; style?: CSSProperties }>;
6
+ export type WrapperProps = FC<{ className?: string; children: ReactNode; style?: CSSProperties }>;
7
7
 
8
8
  export interface LogoTitanProps {
9
9
  mantleFill?: string;
@@ -143,6 +143,8 @@
143
143
  position: absolute;
144
144
  top: @spacing-half;
145
145
  margin-left: @spacing-half;
146
+ min-width: 12px !important;
147
+ min-height: 12px !important;
146
148
  }
147
149
  }
148
150
 
@@ -6,6 +6,7 @@ import SvgAccountInactive from '@servicetitan/anvil2/assets/icons/st/gnav_accoun
6
6
  import { BodyText, Popover, PopoverPropsStrict } from '@servicetitan/design-system';
7
7
  import classNames from 'classnames';
8
8
  import {
9
+ ComponentPropsWithoutRef,
9
10
  FC,
10
11
  HTMLAttributeAnchorTarget,
11
12
  MouseEvent,
@@ -147,6 +148,10 @@ const ProfileDropdownTrigger: FC<ProfileDropdownTriggerProps> = ({
147
148
  );
148
149
  };
149
150
 
151
+ export type ProfileItemContent =
152
+ | { children: string; text?: string }
153
+ | { children: ReactNode; text: string };
154
+
150
155
  export interface ProfileDropdownSectionPropsStrict {
151
156
  children: ReactNode;
152
157
  id: string;
@@ -155,14 +160,15 @@ export interface ProfileDropdownSectionPropsStrict {
155
160
  onClick?(e: MouseEvent): void;
156
161
  }
157
162
 
158
- export interface ProfileDropdownSectionProps extends ProfileDropdownSectionPropsStrict {
159
- [key: string]: any;
160
- }
163
+ export type ProfileDropdownSectionProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> &
164
+ ProfileDropdownSectionPropsStrict &
165
+ ProfileItemContent;
161
166
 
162
167
  export const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = ({
163
168
  children,
164
169
  className,
165
170
  id,
171
+ text,
166
172
  tooltip,
167
173
  onClick,
168
174
  ...rest
@@ -206,9 +212,9 @@ export interface ProfileDropdownLinkPropsStrict {
206
212
  counter?: CounterTagValue;
207
213
  }
208
214
 
209
- export interface ProfileDropdownLinkProps extends ProfileDropdownLinkPropsStrict {
210
- [key: string]: any;
211
- }
215
+ export type ProfileDropdownLinkProps = Omit<ComponentPropsWithoutRef<'a'>, 'children'> &
216
+ ProfileDropdownLinkPropsStrict &
217
+ ProfileItemContent;
212
218
 
213
219
  export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
214
220
  children,
@@ -218,6 +224,7 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
218
224
  counter,
219
225
  tag,
220
226
  target,
227
+ text,
221
228
  to,
222
229
  tooltip,
223
230
  onClick,
@@ -19,7 +19,7 @@ export interface TitanLayoutContextType {
19
19
 
20
20
  export const LayoutContext = createContext<TitanLayoutContextType>({
21
21
  NavigationComponent: DefaultNavLinkComponent,
22
- breakpoint: { name: 'lg', isMobile: false },
22
+ breakpoint: { name: 'lg', isMobile: false, width: 0 },
23
23
  isTitanLayout: false,
24
24
  sidebar: { styles: { popoverContent: {} } },
25
25
  });
@@ -4,29 +4,29 @@
4
4
  @size-links-tiny: 24px;
5
5
 
6
6
  .header {
7
- display: grid;
7
+ --nav-top-content-height: 32px;
8
+ display: flex;
9
+ justify-content: space-between;
8
10
 
9
11
  background-color: @color-white;
10
12
  color: @color-black;
11
- border-bottom: 1px solid @color-neutral-60;
13
+ box-sizing: border-box;
14
+ outline: 1px solid @color-neutral-60;
12
15
 
13
16
  & > * {
14
17
  overflow-y: hidden;
15
18
  }
16
19
 
17
20
  .he-top-left {
18
- grid-column: span 1;
19
21
  display: flex;
20
22
  align-items: center;
21
23
  }
22
24
 
23
25
  .he-top-center {
24
- grid-column: span 1;
26
+ overflow: hidden;
25
27
  }
26
28
 
27
29
  .he-top-right {
28
- grid-column: span 1;
29
-
30
30
  & > * {
31
31
  color: @color-black;
32
32
  }
@@ -90,33 +90,71 @@
90
90
  }
91
91
  }
92
92
 
93
+ .header-mobile {
94
+ padding: @spacing-2 @spacing-0;
95
+ height: var(--nav-offset-top);
96
+
97
+ --nav-top-content-height: 40px;
98
+
99
+ .navigation-link {
100
+ padding: 10px;
101
+ }
102
+
103
+ .he-top-center {
104
+ max-width: unset;
105
+ flex: 1;
106
+ margin-left: @spacing-3;
107
+ margin-right: @spacing-3;
108
+ }
109
+
110
+ .he-top-left {
111
+ margin-left: @spacing-half;
112
+ }
113
+
114
+ .he-top-right {
115
+ margin-right: @spacing-half;
116
+ }
117
+ }
118
+
93
119
  // desktop
94
- @media only screen and (min-width: 768px) {
95
- .header {
96
- grid-template-columns: repeat(3, 1fr);
97
- grid-template-rows: 48px;
120
+ .header-desktop {
121
+ height: var(--nav-offset-top);
122
+ .navigation-link {
123
+ margin: 6px 2px;
124
+ padding: 6px 6px;
98
125
 
99
- .navigation-link {
100
- margin: 6px 2px;
101
- padding: 6px 6px;
126
+ .navigation-item-counter {
127
+ min-width: 12px !important;
128
+ height: 12px !important;
102
129
  }
103
130
  }
104
131
 
105
132
  .he-top-left {
106
133
  padding-left: @spacing-1;
107
134
  }
135
+ .he-top-center {
136
+ flex: 1;
137
+ margin-left: @spacing-2;
138
+ margin-right: @spacing-2;
139
+ max-width: 400px;
140
+ }
108
141
  }
109
142
 
110
- // mobile
111
- @media only screen and (max-width: 768px) {
112
- .header {
143
+ // desktop wide
144
+ @media only screen and (min-width: 1200px) {
145
+ .header-desktop {
146
+ display: grid;
113
147
  grid-template-columns: repeat(3, 1fr);
114
- grid-template-rows: 44px;
148
+ grid-template-rows: 48px;
115
149
 
116
- padding: @spacing-1 @spacing-half;
150
+ .he-top-left,
151
+ .he-top-center,
152
+ .he-top-right {
153
+ grid-column: span 1;
154
+ }
117
155
 
118
- .navigation-link {
119
- padding: 10px;
156
+ .he-top-center {
157
+ max-width: unset;
120
158
  }
121
159
  }
122
160
  }
@@ -134,7 +172,8 @@
134
172
  color: @color-white;
135
173
  font-weight: @font-weight-semibold;
136
174
  font-size: 8px !important;
137
- min-width: 12px;
175
+ min-width: 16px !important;
176
+ height: 16px !important;
138
177
  position: absolute;
139
178
  top: 4px;
140
179
  right: -2px;
@@ -4,6 +4,8 @@ export const heTopLeft: string;
4
4
  export const heTopRight: string;
5
5
  export const heTopRightText: string;
6
6
  export const header: string;
7
+ export const headerDesktop: string;
8
+ export const headerMobile: string;
7
9
  export const navigationIcon: string;
8
10
  export const navigationIconActive: string;
9
11
  export const navigationItemActive: string;
@@ -1,7 +1,7 @@
1
1
  import SvgBurgerMenu from '@servicetitan/anvil2/assets/icons/material/round/menu.svg';
2
2
  import classNames from 'classnames';
3
3
  import { ComponentPropsWithoutRef, FC, ReactElement, ReactNode } from 'react';
4
- import { LayoutPlacementContext, useTitanLayoutContext } from './layout-context';
4
+ import { LayoutPlacementContext } from './layout-context';
5
5
  import { LayoutHeaderNavigationTrigger } from './layout-header-links';
6
6
  import * as Styles from './layout-header.module.less';
7
7
  import { TitanLayoutLogoProps } from './layout-logo';
@@ -25,6 +25,8 @@ export type LayoutHeaderProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
25
25
  logo: ReactElement<TitanLayoutLogoProps>;
26
26
  profile?: ReactElement;
27
27
 
28
+ isMobile: boolean;
29
+ hasNotifications: boolean;
28
30
  onBurgerClick?: (e: MouseEvent) => void;
29
31
  };
30
32
 
@@ -35,22 +37,26 @@ export const LayoutHeader: FC<LayoutHeaderProps> = ({
35
37
  rightClassName,
36
38
  center,
37
39
  centerClassName,
40
+ isMobile,
41
+ hasNotifications,
38
42
  logo,
39
43
  profile,
40
44
  onBurgerClick,
41
45
  ...rest
42
46
  }) => {
43
- const { breakpoint } = useTitanLayoutContext();
44
-
45
47
  return (
46
48
  <LayoutPlacementContext.Provider value="top">
47
49
  <div
48
- className={classNames(Styles.header, className)}
50
+ className={classNames(
51
+ Styles.header,
52
+ isMobile ? Styles.headerMobile : Styles.headerDesktop,
53
+ className
54
+ )}
49
55
  {...rest}
50
56
  data-cy="header-navigation"
51
57
  >
52
58
  <div className={classNames(Styles.heTopLeft)} data-cy="navigation-left">
53
- {breakpoint.isMobile && (
59
+ {isMobile && (
54
60
  <LayoutHeaderNavigationTrigger
55
61
  id="burger"
56
62
  title=""
@@ -58,12 +64,17 @@ export const LayoutHeader: FC<LayoutHeaderProps> = ({
58
64
  iconActive={SvgBurgerMenu}
59
65
  className="m-r-1"
60
66
  onClick={onBurgerClick}
67
+ tag={{ value: hasNotifications }}
61
68
  />
62
69
  )}
63
70
  {logo}
64
71
  </div>
65
72
  <div
66
- className={classNames(Styles.heTopCenter, centerClassName)}
73
+ className={classNames(
74
+ Styles.heTopCenter,
75
+ 'd-f align-items-center justify-content-center',
76
+ centerClassName
77
+ )}
67
78
  data-cy="navigation-center"
68
79
  >
69
80
  {center}
@@ -33,20 +33,27 @@ export const TitanLayoutLogo: FC<TitanLayoutLogoProps> = ({
33
33
  const Wrapper = logoWrapper;
34
34
  const logoSize = isMobile ? 44 : 56;
35
35
  const logoCompanySize = 48;
36
+ const showCompanyTitle = title === true && !isMobile;
36
37
 
37
38
  return (
38
- <div className={classNames('d-f align-items-center', className)}>
39
- {typeof title === 'string' ? (
39
+ <div
40
+ className={classNames(
41
+ 'd-f align-items-center',
42
+ { 'p-t-half': showCompanyTitle },
43
+ className
44
+ )}
45
+ >
46
+ {showCompanyTitle ? (
47
+ <Wrapper>
48
+ <LogoCompanyTitle height={logoCompanySize} />
49
+ </Wrapper>
50
+ ) : typeof title === 'string' ? (
40
51
  <Fragment>
41
52
  <LogoTitan size={logoSize} mantleFill={mantleFill} logoWrapper={Wrapper} />
42
53
  {!isMobile && (
43
54
  <LogoTitanTitle className="c-inherit m-l-1">{title}</LogoTitanTitle>
44
55
  )}
45
56
  </Fragment>
46
- ) : title === true && !isMobile ? (
47
- <Wrapper className="">
48
- <LogoCompanyTitle height={logoCompanySize} />
49
- </Wrapper>
50
57
  ) : (
51
58
  <LogoTitan size={logoSize} mantleFill={mantleFill} logoWrapper={Wrapper} />
52
59
  )}
@@ -18,7 +18,7 @@ export default {
18
18
 
19
19
  export const ProfileDefault = withTitanLayout(
20
20
  <ProfileDropdown>
21
- <ProfileDropdown.Link id="first" to="https://google.com">
21
+ <ProfileDropdown.Link id="first" to="https://google.com" external>
22
22
  first link
23
23
  </ProfileDropdown.Link>
24
24
  <ProfileDropdown.Section id="second" onClick={() => alert('second click')}>
@@ -33,5 +33,14 @@ export const ProfileDefault = withTitanLayout(
33
33
  third link
34
34
  </ProfileDropdown.Link>
35
35
  <ProfileDropdown.Divider />
36
+ <ProfileDropdown.Section
37
+ id="forth"
38
+ onClick={() => alert('forth click')}
39
+ text="Sign Out user"
40
+ >
41
+ Sign Out
42
+ <span className="c-neutral-60 m-l-1">user</span>
43
+ </ProfileDropdown.Section>
44
+ <ProfileDropdown.Divider />
36
45
  </ProfileDropdown>
37
46
  );
@@ -1,8 +1,8 @@
1
1
  import SvgAccountActive from '@servicetitan/anvil2/assets/icons/st/gnav_account_active.svg';
2
2
  import SvgAccountInactive from '@servicetitan/anvil2/assets/icons/st/gnav_account_inactive.svg';
3
3
 
4
- import { FC, useState } from 'react';
5
- import { NavigationComponentContext } from '../../utils/navigation-context';
4
+ import { FC, MouseEvent, useEffect, useState } from 'react';
5
+ import { NavLinkComponentProps, NavigationComponentContext } from '../../utils/navigation-context';
6
6
  import {
7
7
  ProfileDropdown as DesktopProfileDropdown,
8
8
  ProfileDropdownLinkProps,
@@ -17,6 +17,7 @@ import {
17
17
  InternalSideNavigationGroupLink,
18
18
  InternalSideNavigationGroupTrigger,
19
19
  } from './layout-sidebar-links-internal';
20
+ import { useNotificationsContext, useNotificationsState } from './notifications-context';
20
21
 
21
22
  export type {
22
23
  ProfileDropdownProps,
@@ -24,6 +25,18 @@ export type {
24
25
  ProfileDropdownLinkProps,
25
26
  } from '../profile-dropdown/profile-dropdown';
26
27
 
28
+ const ExternalNavComponent: FC<NavLinkComponentProps> = ({
29
+ children,
30
+ isActive,
31
+ to,
32
+ activeClassName,
33
+ ...props
34
+ }) => (
35
+ <a {...props} href={to}>
36
+ {children}
37
+ </a>
38
+ );
39
+
27
40
  const ProfileDropdownContent: FC<ProfileDropdownProps> = props => {
28
41
  const { breakpoint, NavigationComponent } = useTitanLayoutContext();
29
42
  return breakpoint.isMobile ? (
@@ -40,24 +53,37 @@ const MobileProfileDropdown: FC<ProfileDropdownProps & NavigationComponentProps>
40
53
  children,
41
54
  ...props
42
55
  }) => {
56
+ const id = '__profile';
43
57
  const [expanded, setExpanded] = useState(false);
44
- const onExpandToggle = () => setExpanded(!expanded);
58
+ const { hasNotifications, NotificationsContextProvider } = useNotificationsState();
59
+ const { onNotificationsUpdate } = useNotificationsContext();
60
+ const onExpandToggle = (e: MouseEvent<never>) => {
61
+ e.stopPropagation();
62
+ setExpanded(!expanded);
63
+ };
64
+
65
+ useEffect(() => {
66
+ onNotificationsUpdate(id, hasNotifications);
67
+ }, [hasNotifications, onNotificationsUpdate]);
68
+
45
69
  return (
46
- <InternalSideNavigationGroup
47
- id="__profile"
48
- to={undefined}
49
- title="Profile"
50
- icon={SvgAccountInactive}
51
- iconActive={SvgAccountActive}
52
- isActive={expanded}
53
- {...props}
54
- submenuExpanded={expanded}
55
- onExpandToggle={onExpandToggle}
56
- onClick={onExpandToggle}
57
- tag={undefined}
58
- >
59
- {children}
60
- </InternalSideNavigationGroup>
70
+ <NotificationsContextProvider>
71
+ <InternalSideNavigationGroup
72
+ id={id}
73
+ to={undefined}
74
+ title="Profile"
75
+ icon={SvgAccountInactive}
76
+ iconActive={SvgAccountActive}
77
+ isActive={expanded}
78
+ {...props}
79
+ submenuExpanded={expanded}
80
+ onExpandToggle={onExpandToggle}
81
+ onClick={onExpandToggle}
82
+ tag={{ value: hasNotifications }}
83
+ >
84
+ {children}
85
+ </InternalSideNavigationGroup>
86
+ </NotificationsContextProvider>
61
87
  );
62
88
  };
63
89
 
@@ -70,6 +96,18 @@ const ProfileDropdownDivider: FC = () => {
70
96
  );
71
97
  };
72
98
 
99
+ const getText = (children: any, text: any): string | undefined => {
100
+ if (typeof children === 'string') {
101
+ return children;
102
+ }
103
+
104
+ if (typeof text === 'string') {
105
+ return text;
106
+ }
107
+
108
+ return undefined;
109
+ };
110
+
73
111
  const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = props => {
74
112
  const { breakpoint } = useTitanLayoutContext();
75
113
  return breakpoint.isMobile ? (
@@ -78,10 +116,19 @@ const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = props => {
78
116
  <DesktopProfileDropdown.Section {...props} />
79
117
  );
80
118
  };
81
- const MobileProfileDropdownSection: FC<ProfileDropdownSectionProps> = props => {
82
- const title = typeof props.children === 'string' ? props.children : undefined;
83
- return title ? (
84
- <InternalSideNavigationGroupTrigger id={props.id} title={title} onClick={props.onClick} />
119
+ const MobileProfileDropdownSection: FC<ProfileDropdownSectionProps> = ({
120
+ children,
121
+ text,
122
+ tooltip,
123
+ ...props
124
+ }) => {
125
+ const sectionText = getText(children, text);
126
+ return sectionText ? (
127
+ <InternalSideNavigationGroupTrigger
128
+ id={props.id}
129
+ title={sectionText}
130
+ onClick={props.onClick}
131
+ />
85
132
  ) : null;
86
133
  };
87
134
 
@@ -94,17 +141,25 @@ const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = props => {
94
141
  );
95
142
  };
96
143
  const MobileProfileDropdownLink: FC<ProfileDropdownLinkProps & NavigationComponentProps> = ({
144
+ external,
97
145
  to,
146
+ tooltip,
147
+ text,
148
+ children,
98
149
  navigationComponent,
99
150
  ...props
100
151
  }) => {
101
- const title = typeof props.children === 'string' ? props.children : undefined;
102
- return title ? (
152
+ const { onNotificationsUpdate } = useNotificationsContext();
153
+ const linkText = getText(children, text);
154
+ const isExternalLink = external ?? to?.startsWith('http');
155
+ onNotificationsUpdate(props.id, !!props.tag?.value);
156
+
157
+ return linkText ? (
103
158
  <InternalSideNavigationGroupLink
104
159
  {...props}
105
160
  to={to}
106
- title={title}
107
- navigationComponent={navigationComponent}
161
+ title={linkText}
162
+ navigationComponent={isExternalLink ? ExternalNavComponent : navigationComponent}
108
163
  />
109
164
  ) : null;
110
165
  };
@@ -156,7 +156,9 @@ export const InternalSideNavigationLink: FC<InternalSideNavigationLinkProps> = (
156
156
 
157
157
  /** Side Navigation menu trigger (for internal usage) */
158
158
  export const InternalSideNavigationTrigger: FC<
159
- Omit<InternalSideNavigationLinkProps, 'to' | 'navigationComponent'> & { onClick?: () => void }
159
+ Omit<InternalSideNavigationLinkProps, 'to' | 'navigationComponent'> & {
160
+ onClick?: (e: MouseEvent<never>) => void;
161
+ }
160
162
  > = ({ className, dataPrefix, isActive, submenuExpanded, onExpandToggle, onClick, ...props }) => {
161
163
  return (
162
164
  <div
@@ -180,12 +182,22 @@ export const InternalSideNavigationTrigger: FC<
180
182
 
181
183
  export const InternalSideNavigationGroupLink: FC<
182
184
  NavigationSubmenuItemData & NavigationComponentProps
183
- > = ({ id, counter, tag, title, to, isActive, navigationComponent: NavigationComponent }) => {
185
+ > = ({
186
+ id,
187
+ counter,
188
+ tag,
189
+ title,
190
+ to,
191
+ isActive,
192
+ navigationComponent: NavigationComponent,
193
+ ...rest
194
+ }) => {
184
195
  return (
185
196
  <NavigationComponent
197
+ key={id}
186
198
  data-cy={`navigation-item-${id}`}
187
199
  data-pendo={`navigation-item-${id}`}
188
- key={id}
200
+ {...rest}
189
201
  to={to}
190
202
  className={classNames(Styles.submenuItem, Styles.submenuLink, {
191
203
  [Styles.submenuLinkActive]: isActive === true,
@@ -201,12 +213,13 @@ export const InternalSideNavigationGroupLink: FC<
201
213
 
202
214
  export const InternalSideNavigationGroupTrigger: FC<
203
215
  Omit<NavigationSubmenuItemData, 'to'> & { onClick?: (e: MouseEvent<any>) => void }
204
- > = ({ id, counter, onClick, tag, title, isActive }) => {
216
+ > = ({ id, counter, onClick, tag, title, isActive, ...rest }) => {
205
217
  return (
206
218
  <div
207
219
  data-cy={`navigation-item-${id}`}
208
220
  data-pendo={`navigation-item-${id}`}
209
221
  key={id}
222
+ {...rest}
210
223
  className={classNames(Styles.submenuItem, {
211
224
  [Styles.submenuLink]: !!onClick,
212
225
  [Styles.submenuLinkActive]: isActive === true,
@@ -231,7 +244,7 @@ export const InternalSideNavigationGroup: FC<
231
244
  onExpandToggle?: (e: MouseEvent<never>) => void;
232
245
  tag: BadgeTagProps | undefined;
233
246
  to: NavigationItemData['to'] | undefined;
234
- onClick?: () => void;
247
+ onClick?: (e: MouseEvent<never>) => void;
235
248
  }
236
249
  > = ({ children, submenuExpanded, to, onExpandToggle, onClick, ...props }) => {
237
250
  return (