@servicetitan/navigation 8.2.1 → 9.0.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.
- package/dist/components/counter-tag.d.ts +2 -4
- package/dist/components/counter-tag.d.ts.map +1 -1
- package/dist/components/counter-tag.js +3 -4
- package/dist/components/counter-tag.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-content.d.ts +2 -2
- package/dist/components/header-navigation/header-navigation-content.d.ts.map +1 -1
- package/dist/components/header-navigation/header-navigation-content.js +4 -4
- package/dist/components/header-navigation/header-navigation-content.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-extra.stories.js +3 -3
- package/dist/components/header-navigation/header-navigation-extra.stories.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-links.js +4 -4
- package/dist/components/header-navigation/header-navigation-links.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-stacked.stories.js +1 -1
- package/dist/components/header-navigation/header-navigation-stacked.stories.js.map +1 -1
- package/dist/components/header-navigation/header-navigation.module.less +2 -1
- package/dist/components/header-navigation/header-navigation.stories.js +1 -1
- package/dist/components/header-navigation/header-navigation.stories.js.map +1 -1
- package/dist/components/left-navigation/header-navigation-tiny-links.d.ts +1 -1
- package/dist/components/left-navigation/header-navigation-tiny-links.d.ts.map +1 -1
- package/dist/components/left-navigation/header-navigation-tiny-links.js +6 -6
- package/dist/components/left-navigation/header-navigation-tiny-links.js.map +1 -1
- package/dist/components/left-navigation/header-navigation-tiny.stories.js +2 -2
- package/dist/components/left-navigation/header-navigation-tiny.stories.js.map +1 -1
- package/dist/components/left-navigation/side-navigation.d.ts +8 -4
- package/dist/components/left-navigation/side-navigation.d.ts.map +1 -1
- package/dist/components/left-navigation/side-navigation.js +31 -20
- package/dist/components/left-navigation/side-navigation.js.map +1 -1
- package/dist/components/left-navigation/side-navigation.module.less +20 -12
- package/dist/components/left-navigation/side-navigation.stories.js +3 -3
- package/dist/components/left-navigation/side-navigation.stories.js.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.d.ts +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.js +3 -3
- package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
- package/dist/test/data.d.ts.map +1 -1
- package/dist/test/data.js +7 -6
- package/dist/test/data.js.map +1 -1
- package/dist/utils/navigation.d.ts +9 -5
- package/dist/utils/navigation.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/components/counter-tag.tsx +12 -9
- package/src/components/header-navigation/header-navigation-content.tsx +7 -7
- package/src/components/header-navigation/header-navigation-extra.stories.tsx +3 -3
- package/src/components/header-navigation/header-navigation-links.tsx +4 -4
- package/src/components/header-navigation/header-navigation-stacked.stories.tsx +2 -2
- package/src/components/header-navigation/header-navigation.module.less +2 -1
- package/src/components/header-navigation/header-navigation.stories.tsx +2 -2
- package/src/components/left-navigation/header-navigation-tiny-links.tsx +8 -8
- package/src/components/left-navigation/header-navigation-tiny.stories.tsx +2 -2
- package/src/components/left-navigation/side-navigation.module.less +20 -12
- package/src/components/left-navigation/side-navigation.module.less.d.ts +1 -0
- package/src/components/left-navigation/side-navigation.stories.tsx +4 -4
- package/src/components/left-navigation/side-navigation.tsx +62 -41
- package/src/components/profile-dropdown/profile-dropdown.tsx +6 -9
- package/src/test/data.tsx +11 -10
- package/src/utils/navigation.ts +10 -5
|
@@ -9,13 +9,13 @@ import { withTooltip } from './with-tooltip';
|
|
|
9
9
|
|
|
10
10
|
/** Content for navigation items */
|
|
11
11
|
export const HeaderNavigationItemContent: FC<{
|
|
12
|
-
|
|
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
|
-
}> = ({
|
|
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
|
-
{!!
|
|
35
|
+
{!!tag && (
|
|
36
36
|
<CounterTag
|
|
37
|
-
data={
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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"
|
|
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-
|
|
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
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Page, Sidebar } from '@servicetitan/design-system';
|
|
2
2
|
import { ComponentType, useState } from 'react';
|
|
3
3
|
import { LocationInfo, items, withAnvil, withMemoryRouter } from '../../test/data';
|
|
4
|
-
import { SideNavigation } from './';
|
|
4
|
+
import { SideNavigation, SideNavigationExpandedState } from './';
|
|
5
5
|
|
|
6
6
|
const layout = (Story: ComponentType) => {
|
|
7
7
|
return (
|
|
@@ -23,7 +23,7 @@ export default {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export const DefaultSideNavigation = () => {
|
|
26
|
-
const [expanded, setExpanded] = useState(
|
|
26
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
27
27
|
return (
|
|
28
28
|
<SideNavigation
|
|
29
29
|
expanded={expanded}
|
|
@@ -50,7 +50,7 @@ export const DefaultSideNavigation = () => {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
export const SideNavigationLinksOnly = () => {
|
|
53
|
-
const [expanded, setExpanded] = useState(
|
|
53
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
54
54
|
return (
|
|
55
55
|
<SideNavigation
|
|
56
56
|
expanded={expanded}
|
|
@@ -76,7 +76,7 @@ export const SideNavigationLinksOnly = () => {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const SideNavigationWithSubmenu = () => {
|
|
79
|
-
const [expanded, setExpanded] = useState(
|
|
79
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
80
80
|
return (
|
|
81
81
|
<SideNavigation
|
|
82
82
|
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?:
|
|
44
|
+
expanded?: SideNavigationExpandedState;
|
|
41
45
|
/** expand change handler */
|
|
42
|
-
onExpandedChange(expanded:
|
|
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?:
|
|
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 =
|
|
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
|
-
{!!
|
|
184
|
-
|
|
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={
|
|
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,
|
|
@@ -205,28 +205,44 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
const submenuPopoverStyles = { '--background-color-strong': '#24323C' } as CSSProperties;
|
|
208
|
+
const getFirstSubmenuLinkTo = (
|
|
209
|
+
submenu: HeaderNavigationItemSubmenu | undefined,
|
|
210
|
+
defaultTo: string
|
|
211
|
+
) => submenu?.groups[0]?.links?.[0].to ?? defaultTo;
|
|
208
212
|
|
|
209
213
|
/** Side Navigation menu item */
|
|
210
|
-
const SideNavigationGroupItem: FC<
|
|
211
|
-
|
|
214
|
+
const SideNavigationGroupItem: FC<
|
|
215
|
+
SideNavigationItemProps & {
|
|
216
|
+
onExpandedChange: undefined | ((expanded: SideNavigationExpandedState) => void);
|
|
217
|
+
}
|
|
218
|
+
> = ({ onExpandedChange, ...props }) => {
|
|
212
219
|
const triggerClick = useCallback(
|
|
213
220
|
(e: MouseEvent<HTMLDivElement>) => {
|
|
214
221
|
e.stopPropagation();
|
|
215
222
|
e.preventDefault();
|
|
216
223
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
224
|
+
const isSubmenuExpanded = props.expanded?.submenus?.includes(props.id);
|
|
225
|
+
onExpandedChange?.({
|
|
226
|
+
bar: !!props.expanded?.bar,
|
|
227
|
+
submenus: [
|
|
228
|
+
...(props.expanded?.submenus?.filter(id => id !== props.id) ?? []),
|
|
229
|
+
...(isSubmenuExpanded ? [] : [props.id]),
|
|
230
|
+
],
|
|
231
|
+
});
|
|
220
232
|
},
|
|
221
|
-
[props.expanded]
|
|
233
|
+
[props.id, props.expanded, onExpandedChange]
|
|
222
234
|
);
|
|
223
235
|
|
|
224
|
-
|
|
236
|
+
const tag = props.submenu?.groups.some(group => group.links.some(link => !!link.tag))
|
|
237
|
+
? true
|
|
238
|
+
: props.tag;
|
|
239
|
+
|
|
240
|
+
return props.expanded?.bar ? (
|
|
225
241
|
<Fragment>
|
|
226
242
|
<div onClickCapture={triggerClick}>
|
|
227
|
-
<SideNavigationItem {...props}
|
|
243
|
+
<SideNavigationItem {...props} tag={tag} />
|
|
228
244
|
</div>
|
|
229
|
-
<Collapsible open={
|
|
245
|
+
<Collapsible open={props.expanded?.submenus?.includes(props.id)} animate>
|
|
230
246
|
<div className={Styles.submenu}>
|
|
231
247
|
<SideNavigationGroupContent
|
|
232
248
|
groups={props.submenu?.groups ?? []}
|
|
@@ -239,8 +255,12 @@ const SideNavigationGroupItem: FC<SideNavigationItemProps> = ({ ...props }) => {
|
|
|
239
255
|
<Popover placement="right-start" openOnHover>
|
|
240
256
|
<Popover.Trigger>
|
|
241
257
|
{(triggerProps: PopoverTriggerProps) => (
|
|
242
|
-
<div {...triggerProps}
|
|
243
|
-
<SideNavigationItem
|
|
258
|
+
<div {...triggerProps}>
|
|
259
|
+
<SideNavigationItem
|
|
260
|
+
{...props}
|
|
261
|
+
to={getFirstSubmenuLinkTo(props.submenu, props.to)}
|
|
262
|
+
tag={tag}
|
|
263
|
+
/>
|
|
244
264
|
</div>
|
|
245
265
|
)}
|
|
246
266
|
</Popover.Trigger>
|
|
@@ -264,17 +284,14 @@ const SideNavigationGroupContent: FC<HeaderNavigationItemSubmenu & NavigationCom
|
|
|
264
284
|
}) => {
|
|
265
285
|
return (
|
|
266
286
|
<Fragment>
|
|
267
|
-
{groups.reduce((out, group) => {
|
|
287
|
+
{groups.reduce((out, group, index) => {
|
|
268
288
|
if (!group.links.length) {
|
|
269
289
|
return out;
|
|
270
290
|
}
|
|
271
291
|
|
|
292
|
+
const key = `:group:${index}:title`;
|
|
272
293
|
out.push(
|
|
273
|
-
<Text
|
|
274
|
-
key=":group:title"
|
|
275
|
-
variant="eyebrow"
|
|
276
|
-
className={Styles.submenuGroupHeader}
|
|
277
|
-
>
|
|
294
|
+
<Text key={key} variant="eyebrow" className={Styles.submenuGroupHeader}>
|
|
278
295
|
{group.title}
|
|
279
296
|
</Text>
|
|
280
297
|
);
|
|
@@ -293,8 +310,9 @@ const SideNavigationGroupContent: FC<HeaderNavigationItemSubmenu & NavigationCom
|
|
|
293
310
|
</Fragment>
|
|
294
311
|
);
|
|
295
312
|
};
|
|
296
|
-
const SideNavigationGroupLink: FC<
|
|
313
|
+
const SideNavigationGroupLink: FC<HeaderNavigationItemSubmenuLink & NavigationComponentProps> = ({
|
|
297
314
|
id,
|
|
315
|
+
tag,
|
|
298
316
|
title,
|
|
299
317
|
to,
|
|
300
318
|
isActive,
|
|
@@ -312,22 +330,25 @@ const SideNavigationGroupLink: FC<HeaderNavigationItemLinkProps & NavigationComp
|
|
|
312
330
|
isActive={typeof isActive === 'function' ? isActive : undefined}
|
|
313
331
|
activeClassName={Styles.submenuLinkActive}
|
|
314
332
|
>
|
|
315
|
-
{title}
|
|
333
|
+
<span>{title}</span>
|
|
334
|
+
{!!tag && <CounterTag data={tag} className={Styles.submenuLinkCounter} />}
|
|
316
335
|
</NavigationComponent>
|
|
317
336
|
);
|
|
318
337
|
};
|
|
319
338
|
|
|
320
339
|
/** Side Navigation options toggle */
|
|
321
340
|
export const SideNavigationOptionsToggle: FC<{
|
|
322
|
-
expanded?:
|
|
323
|
-
onExpandedChange(expanded:
|
|
341
|
+
expanded?: SideNavigationExpandedState;
|
|
342
|
+
onExpandedChange?(expanded: SideNavigationExpandedState): void;
|
|
324
343
|
}> = ({ expanded, onExpandedChange }) =>
|
|
325
344
|
withTooltip(
|
|
326
345
|
<div
|
|
327
346
|
data-cy="navigation-left-options"
|
|
328
347
|
data-pendo="navigation-left-options"
|
|
329
348
|
className={classNames(Styles.optionsItem)}
|
|
330
|
-
onClick={() =>
|
|
349
|
+
onClick={() =>
|
|
350
|
+
onExpandedChange?.({ bar: !expanded?.bar, submenus: expanded?.submenus })
|
|
351
|
+
}
|
|
331
352
|
>
|
|
332
353
|
<div className={Styles.optionsIconWrapper}>
|
|
333
354
|
<Icon className={Styles.optionsIcon} svg={expanded ? SvgCollapse : SvgExpand} />
|
|
@@ -192,7 +192,7 @@ export interface ProfileDropdownLinkPropsStrict {
|
|
|
192
192
|
external?: boolean;
|
|
193
193
|
target?: HTMLAttributeAnchorTarget;
|
|
194
194
|
to?: string;
|
|
195
|
-
|
|
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
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
258
|
+
{tagElement}
|
|
262
259
|
</a>
|
|
263
260
|
);
|
|
264
261
|
};
|
package/src/test/data.tsx
CHANGED
|
@@ -40,8 +40,8 @@ import { MemoryRouter, useHistory, useLocation } from 'react-router-dom';
|
|
|
40
40
|
import { HeaderNavigationTrigger } from '../components/links';
|
|
41
41
|
import {
|
|
42
42
|
HeaderNavigationItemData,
|
|
43
|
-
HeaderNavigationItemLinkProps,
|
|
44
43
|
HeaderNavigationItemSubmenuGroup,
|
|
44
|
+
HeaderNavigationItemSubmenuLink,
|
|
45
45
|
NavLinkComponentProps,
|
|
46
46
|
} from '../utils/navigation';
|
|
47
47
|
import { NavigationComponentContext } from '../utils/navigation-context';
|
|
@@ -145,8 +145,8 @@ const getItem = (
|
|
|
145
145
|
|
|
146
146
|
const getSubItem = (
|
|
147
147
|
id: string,
|
|
148
|
-
data: Partial<
|
|
149
|
-
):
|
|
148
|
+
data: Partial<HeaderNavigationItemSubmenuLink>
|
|
149
|
+
): HeaderNavigationItemSubmenuLink => ({
|
|
150
150
|
id,
|
|
151
151
|
to: id,
|
|
152
152
|
title: id[0].toUpperCase() + id.substring(1),
|
|
@@ -155,7 +155,7 @@ const getSubItem = (
|
|
|
155
155
|
|
|
156
156
|
const getGroup = (
|
|
157
157
|
title: string,
|
|
158
|
-
links:
|
|
158
|
+
links: HeaderNavigationItemSubmenuLink[]
|
|
159
159
|
): HeaderNavigationItemSubmenuGroup => ({
|
|
160
160
|
title,
|
|
161
161
|
links,
|
|
@@ -176,7 +176,7 @@ export const items = {
|
|
|
176
176
|
iconName: 'local_phone',
|
|
177
177
|
icon: SvgCalls,
|
|
178
178
|
iconActive: SvgCallsActive,
|
|
179
|
-
|
|
179
|
+
tag: 12,
|
|
180
180
|
}),
|
|
181
181
|
dashboard: getItem('dashboard', {
|
|
182
182
|
iconName: 'odometer',
|
|
@@ -187,7 +187,7 @@ export const items = {
|
|
|
187
187
|
iconName: 'location_disabled',
|
|
188
188
|
icon: SvgDispatch,
|
|
189
189
|
iconActive: SvgDispatchActive,
|
|
190
|
-
|
|
190
|
+
tag: 1,
|
|
191
191
|
}),
|
|
192
192
|
fleet: getItem('fleet', {
|
|
193
193
|
iconName: 'fleet-pro',
|
|
@@ -241,6 +241,7 @@ export const items = {
|
|
|
241
241
|
iconName: 'assignment',
|
|
242
242
|
icon: SvgAccounting,
|
|
243
243
|
iconActive: SvgAccountingActive,
|
|
244
|
+
tag: true,
|
|
244
245
|
}),
|
|
245
246
|
accountingWithSubmenu: getItem('accounting', {
|
|
246
247
|
iconName: 'assignment',
|
|
@@ -250,9 +251,9 @@ export const items = {
|
|
|
250
251
|
groups: [
|
|
251
252
|
getGroup('Accounts Receivable', [
|
|
252
253
|
getSubItem('ar', { title: 'AR Management' }),
|
|
253
|
-
getSubItem('export', { title: 'Batch/Export Transactions' }),
|
|
254
|
-
getSubItem('invoices', { title: 'Invoices' }),
|
|
255
|
-
getSubItem('payments', { title: 'Customer Payments' }),
|
|
254
|
+
getSubItem('export', { title: 'Batch/Export Transactions', tag: true }),
|
|
255
|
+
getSubItem('invoices', { title: 'Invoices', tag: 3 }),
|
|
256
|
+
getSubItem('payments', { title: 'Customer Payments', tag: 2 }),
|
|
256
257
|
getSubItem('deposits', { title: 'Bank Deposits' }),
|
|
257
258
|
]),
|
|
258
259
|
getGroup('Accounts Payable', [getSubItem('bills', { title: 'Bills' })]),
|
|
@@ -306,7 +307,7 @@ export const CallsNavigationTrigger = () => {
|
|
|
306
307
|
<HeaderNavigationTrigger
|
|
307
308
|
id="dialpad"
|
|
308
309
|
iconName="phone"
|
|
309
|
-
|
|
310
|
+
tag={2}
|
|
310
311
|
icon={SvgPhone}
|
|
311
312
|
iconActive={SvgPhoneActive}
|
|
312
313
|
onClick={() => setOpen(!open)}
|
package/src/utils/navigation.ts
CHANGED
|
@@ -25,8 +25,8 @@ export interface HeaderNavigationItemData extends HeaderNavigationItemLinkProps
|
|
|
25
25
|
/** icon component of item (<svg />) */
|
|
26
26
|
iconComponent?: FC;
|
|
27
27
|
|
|
28
|
-
/** item
|
|
29
|
-
|
|
28
|
+
/** item tag (optional). shown if it is set and true or greater than 0 */
|
|
29
|
+
tag?: CounterTagPropsType;
|
|
30
30
|
|
|
31
31
|
/** class name of link item */
|
|
32
32
|
className?: string;
|
|
@@ -54,12 +54,17 @@ export interface HeaderNavigationItemSubmenu {
|
|
|
54
54
|
groups: HeaderNavigationItemSubmenuGroup[];
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
export interface HeaderNavigationItemSubmenuLink extends HeaderNavigationItemLinkProps {
|
|
58
|
+
/** item tag (optional). shown if it is set and true or greater than 0 */
|
|
59
|
+
tag?: CounterTagPropsType;
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
export interface HeaderNavigationItemSubmenuGroup {
|
|
58
63
|
/** submenu group title */
|
|
59
64
|
title: string;
|
|
60
65
|
|
|
61
66
|
/** submenu group links */
|
|
62
|
-
links:
|
|
67
|
+
links: HeaderNavigationItemSubmenuLink[];
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
export interface NavLinkComponentPropsStrict {
|
|
@@ -91,8 +96,8 @@ export interface HeaderNavigationTriggerPropsStrict {
|
|
|
91
96
|
labelClassName?: string;
|
|
92
97
|
/** isActive */
|
|
93
98
|
isActive?: boolean;
|
|
94
|
-
/** counter shown for item */
|
|
95
|
-
|
|
99
|
+
/** counter tag shown for item */
|
|
100
|
+
tag?: CounterTagPropsType;
|
|
96
101
|
/** counter component class name */
|
|
97
102
|
counterClassName?: string;
|
|
98
103
|
/** icon component class name */
|