Skip to content

Commit 6aaa75a

Browse files
authored
Merge pull request #2902 from devtron-labs/feat/side-navigation
feat: add side navigation for application management Configurations and Global Configurations
2 parents 1953708 + 0e4ee78 commit 6aaa75a

File tree

17 files changed

+320
-368
lines changed

17 files changed

+320
-368
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.20.0-pre-4",
7+
"@devtron-labs/devtron-fe-common-lib": "1.20.0-pre-5",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",

src/Pages-Devtron-2.0/ApplicationManagement/Configurations/ConfigurationsRouter.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import { lazy } from 'react'
22
import { Redirect, Route, Switch } from 'react-router-dom'
33

4-
import { noop, PageHeader, SERVER_MODE, URLS as COMMON_URLS, useMainContext } from '@devtron-labs/devtron-fe-common-lib'
4+
import {
5+
noop,
6+
PageHeader,
7+
SERVER_MODE,
8+
SideNavigation,
9+
URLS as COMMON_URLS,
10+
useMainContext,
11+
} from '@devtron-labs/devtron-fe-common-lib'
512

613
import ChartRepo from '@Components/chartRepo/ChartRepo'
714
import { importComponentFromFELibrary } from '@Components/common'
15+
import { APPLICATION_MANAGEMENT_CONFIGURATIONS } from '@Components/Navigation'
816
import { AddNotification } from '@Components/notifications/AddNotification'
917
import { URLS } from '@Config/routes'
1018

@@ -35,11 +43,13 @@ export const Configurations = () => {
3543

3644
return (
3745
<>
38-
{/* TODO Rohit: Update with Application Management Header */}
46+
{/* TODO: Update with Application Management Header */}
3947
<PageHeader headerName="Application Management / Configurations" />
4048
<div className="application-management-configurations dc__grid flex-grow-1 dc__overflow-auto">
41-
<div className="border__primary--right">SidePanel</div>
42-
<div className="bg__secondary">
49+
<div className="py-12 pl-8 pr-7 border__primary--right">
50+
<SideNavigation list={APPLICATION_MANAGEMENT_CONFIGURATIONS} />
51+
</div>
52+
<div className="bg__secondary dc__overflow-auto">
4353
<Switch>
4454
{isFeatureGitOpsEnabled && (
4555
<Route

src/Pages/Shared/CommandBar/CommandBarBackdrop.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,18 @@ const CommandBarBackdrop = ({ handleClose, isLoadingAppList, appList }: CommandB
304304
)
305305

306306
return (
307-
<Backdrop onEscape={noop} onClick={handleClose} deactivateFocusOnEscape={!!searchText}>
307+
<Backdrop
308+
onEscape={noop}
309+
onClick={handleClose}
310+
deactivateFocusOnEscape={!!searchText}
311+
returnFocusOnDeactivate={false}
312+
>
308313
<div
309314
onClick={stopPropagation}
310315
className="dc__mxw-720 mxh-500 flexbox-col dc__overflow-hidden dc__content-space br-12 bg__modal--primary command-bar__container w-100 h-100"
311316
>
312317
<div className="flexbox-col dc__overflow-hidden flex-grow-1">
313-
<div className="px-20 py-8">
318+
<div className="px-16 py-12">
314319
<SearchBar
315320
inputProps={{
316321
autoFocus: true,

src/Pages/Shared/CommandBar/CommandGroup.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const CommandGroup = ({
6464
return (
6565
<div className={`flexbox-col ${baseIndex === 0 ? 'px-8 pb-8 pt-16' : 'p-8'}`}>
6666
<div className="flexbox px-16 py-6 dc__gap-4 dc__position-sticky dc__top-0 dc__zi-1 bg__modal--primary">
67-
<h2 className="m-0 cn-7 fs-13 fw-6 lh-20 dc__uppercase font-ibm-plex-mono" id={id}>
67+
<h2 className="m-0 cn-7 fs-13 fw-6 lh-20 dc__uppercase font-ibm-plex-sans" id={id}>
6868
{title}
6969
</h2>
7070
</div>

src/components/Navigation/NavGroup.tsx

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@ import { Icon, Tooltip } from '@devtron-labs/devtron-fe-common-lib'
55

66
import { NavGroupProps } from './types'
77

8-
export const NavGroup = ({ title, icon, isSelected, isExpanded, to, disabled, onClick, tooltip }: NavGroupProps) => {
8+
export const NavGroup = ({
9+
title,
10+
icon,
11+
isSelected,
12+
isExpanded,
13+
to,
14+
disabled,
15+
onClick,
16+
tooltip,
17+
onHover,
18+
showTooltip,
19+
}: NavGroupProps) => {
920
const shouldRenderNavLink = !!to
1021

11-
const className = `nav-group dc__transparent px-10 py-8 dc__position-rel ${isSelected ? 'is-selected' : ''} ${isExpanded ? 'is-expanded' : ''} ${disabled ? 'dc__disabled' : ''}`
22+
const className = `nav-group dc__transparent px-10 py-8 ${isSelected ? 'is-selected' : ''} ${isExpanded ? 'is-expanded' : ''} ${disabled ? 'dc__disabled' : ''}`
1223

1324
const content = (
14-
<>
15-
<span className="nav-group__icon-container flex p-8 br-8">
16-
<Icon name={icon} color="white" size={20} />
17-
</span>
18-
<span className="nav-group__divider-container dc__position-abs dc__left-0 dc__bottom-0 flex w-100">
19-
<span className="border__sidenav-primary--bottom w-24" />
20-
</span>
21-
</>
25+
<span className="nav-group__icon-container flex p-8 br-8">
26+
<Icon name={icon} color="white" size={20} />
27+
</span>
2228
)
2329

2430
const handleNavLinkClick = (e: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) => {
@@ -30,36 +36,38 @@ export const NavGroup = ({ title, icon, isSelected, isExpanded, to, disabled, on
3036
}
3137

3238
return (
33-
<Tooltip
34-
alwaysShowTippyOnHover
35-
content={tooltip || (disabled ? `Coming Soon - ${title}` : title)}
36-
placement="right"
37-
className="nav-group__tooltip no-content-padding"
38-
>
39-
{shouldRenderNavLink ? (
40-
<NavLink
41-
to={to}
42-
className={className}
43-
activeClassName="is-selected"
44-
aria-disabled={disabled}
45-
onClick={handleNavLinkClick}
46-
>
47-
{content}
48-
</NavLink>
49-
) : (
50-
<span>
51-
<button
52-
type="button"
53-
aria-label={title}
54-
aria-disabled={disabled}
55-
disabled={disabled}
39+
<div className="flex" onMouseEnter={() => onHover?.(true)} onMouseLeave={() => onHover?.(false)}>
40+
<Tooltip
41+
alwaysShowTippyOnHover={showTooltip}
42+
content={showTooltip ? tooltip || (disabled ? `Coming Soon - ${title}` : title) : null}
43+
placement="right"
44+
className="nav-group__tooltip no-content-padding"
45+
>
46+
{shouldRenderNavLink ? (
47+
<NavLink
48+
to={to}
5649
className={className}
57-
onClick={onClick}
50+
activeClassName="is-selected"
51+
aria-disabled={disabled}
52+
onClick={handleNavLinkClick}
5853
>
5954
{content}
60-
</button>
61-
</span>
62-
)}
63-
</Tooltip>
55+
</NavLink>
56+
) : (
57+
<span>
58+
<button
59+
type="button"
60+
aria-label={title}
61+
aria-disabled={disabled}
62+
disabled={disabled}
63+
className={className}
64+
onClick={onClick}
65+
>
66+
{content}
67+
</button>
68+
</span>
69+
)}
70+
</Tooltip>
71+
</div>
6472
)
6573
}

src/components/Navigation/Navigation.tsx

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useEffect, useMemo, useRef, useState } from 'react'
17+
import { MouseEvent, useEffect, useMemo, useRef, useState } from 'react'
1818
import { useLocation } from 'react-router-dom'
1919

2020
import {
@@ -51,7 +51,7 @@ export const Navigation = ({
5151
pageState,
5252
}: NavigationProps) => {
5353
// STATES
54-
const [clickedNavGroup, setClickedNavGroup] = useState<NavigationGroupType | null>(null)
54+
const [hoveredNavGroup, setHoveredNavGroup] = useState<NavigationGroupType | null>(null)
5555
const [searchText, setSearchText] = useState('')
5656
const [showCommandBar, setShowCommandBar] = useState(false)
5757

@@ -61,6 +61,7 @@ export const Navigation = ({
6161
// REFS
6262
const securityTrivyModuleTimeout = useRef<NodeJS.Timeout>(null)
6363
const securityClairModuleTimeout = useRef<NodeJS.Timeout>(null)
64+
const timeoutRef = useRef<NodeJS.Timeout>(null)
6465

6566
useEffect(
6667
() => () => {
@@ -139,8 +140,10 @@ export const Navigation = ({
139140
[pathname],
140141
)
141142

142-
const currentNavGroup = clickedNavGroup || selectedNavGroup
143-
const isExpanded = !!clickedNavGroup
143+
// The current navigation group is the one that is hovered or the one that is active, \
144+
// this is used to determine which nav group items are to be shown in expanded state.
145+
const currentNavGroup = hoveredNavGroup || selectedNavGroup
146+
const isExpanded = !!hoveredNavGroup
144147

145148
const navItems = useMemo<NavigationGroupType['items']>(
146149
() => (currentNavGroup ? filterNavigationItems(currentNavGroup.items, searchText) : []),
@@ -158,7 +161,7 @@ export const Navigation = ({
158161
) {
159162
e.preventDefault()
160163
}
161-
setClickedNavGroup(navItem)
164+
setHoveredNavGroup(navItem)
162165
setSearchText('')
163166
}
164167

@@ -168,10 +171,32 @@ export const Navigation = ({
168171
if (searchText && !forceClose) {
169172
return
170173
}
171-
setClickedNavGroup(null)
174+
setHoveredNavGroup(null)
172175
setSearchText('')
173176
}
174177

178+
const handleNavGroupHover = (navGroup: typeof hoveredNavGroup) => (isHovered: boolean) => {
179+
clearTimeout(timeoutRef.current)
180+
181+
if (isHovered) {
182+
if (!hoveredNavGroup) {
183+
setHoveredNavGroup(navGroup)
184+
return
185+
}
186+
187+
timeoutRef.current = setTimeout(() => {
188+
setHoveredNavGroup(navGroup)
189+
setSearchText('')
190+
}, 50)
191+
}
192+
}
193+
194+
const handleOpenExpandedNavigation = (e: MouseEvent<HTMLDivElement>) => {
195+
if (!hoveredNavGroup && e.target === e.currentTarget) {
196+
setHoveredNavGroup(selectedNavGroup)
197+
}
198+
}
199+
175200
const handleOpenCommandBar = () => {
176201
setShowCommandBar(true)
177202
handleCloseExpandedNavigation(true)()
@@ -182,32 +207,44 @@ export const Navigation = ({
182207
<div className="navigation dc__position-rel">
183208
<nav
184209
className={`navigation__default dc__position-rel dc__grid dc__overflow-hidden h-100 ${isExpanded ? 'is-expanded' : ''}`}
210+
onMouseEnter={handleOpenExpandedNavigation}
185211
>
186212
<NavigationLogo />
187213
<NavGroup
188214
title="Search"
189215
icon="ic-magnifying-glass"
190216
isExpanded={isExpanded}
191217
onClick={handleOpenCommandBar}
218+
showTooltip
192219
tooltip={
193220
<span className="flex dc__gap-2">
194221
Search&nbsp;
195222
<KeyboardShortcut keyboardKey="Meta" />
196223
<KeyboardShortcut keyboardKey="K" />
197224
</span>
198225
}
226+
onHover={handleCloseExpandedNavigation(true)}
227+
/>
228+
<NavGroup
229+
title="Overview"
230+
icon="ic-speedometer"
231+
to="/dummy-url"
232+
isExpanded={isExpanded}
233+
onHover={handleCloseExpandedNavigation(true)}
234+
disabled
235+
showTooltip
199236
/>
200-
<NavGroup title="Overview" icon="ic-speedometer" to="/dummy-url" isExpanded={isExpanded} />
201237
{NAVIGATION_LIST.map((item) => (
202238
<NavGroup
203239
key={item.id}
204240
title={item.title}
205241
icon={item.icon}
206242
disabled={item.disabled}
207243
isExpanded={isExpanded}
208-
isSelected={clickedNavGroup?.id === item.id || selectedNavGroup?.id === item.id}
244+
isSelected={hoveredNavGroup?.id === item.id || selectedNavGroup?.id === item.id}
209245
onClick={handleNavGroupClick(item)}
210246
to={findActiveNavigationItemOfNavGroup(item.items)?.href}
247+
onHover={handleNavGroupHover(item)}
211248
/>
212249
))}
213250
{!window._env_.K8S_CLIENT && !isAirgapped && showStackManager && (
@@ -216,6 +253,8 @@ export const Navigation = ({
216253
icon="ic-stack"
217254
to={URLS.STACK_MANAGER_ABOUT}
218255
isExpanded={isExpanded}
256+
onHover={handleCloseExpandedNavigation(true)}
257+
showTooltip
219258
/>
220259
)}
221260
</nav>
@@ -249,13 +288,21 @@ export const Navigation = ({
249288
inputProps={{ autoFocus: true }}
250289
/>
251290
<div className="flex-grow-1 dc__overflow-auto">
252-
{navItems
253-
.filter(({ forceHideEnvKey, hideNav }) =>
254-
forceHideEnvKey ? window._env_[forceHideEnvKey] : !hideNav,
255-
)
256-
.map((item) => (
257-
<NavItem key={item.title} {...item} hasSearchText={!!searchText} />
258-
))}
291+
{navItems.length ? (
292+
navItems
293+
.filter(({ forceHideEnvKey, hideNav }) =>
294+
forceHideEnvKey ? window._env_[forceHideEnvKey] : !hideNav,
295+
)
296+
.map((item) => (
297+
<NavItem
298+
key={item.title}
299+
{...item}
300+
hasSearchText={!!searchText}
301+
/>
302+
))
303+
) : (
304+
<span className="fs-13 lh-20 text__sidenav">No matching results</span>
305+
)}
259306
</div>
260307
</div>
261308
)}

0 commit comments

Comments
 (0)