Skip to content

Commit 370a95a

Browse files
committed
fix: make sidebar dropdown arrow togglable
Signed-off-by: Pranav-IIITM <jogdandpranav2007@gmail.com>
1 parent 12d692d commit 370a95a

2 files changed

Lines changed: 94 additions & 27 deletions

File tree

src/components/Layout/Sidebar/SidebarLink.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ interface SidebarLinkProps {
2929
isExpanded?: boolean;
3030
hideArrow?: boolean;
3131
isPending: boolean;
32+
onToggle?: () => void;
3233
}
3334

3435
export function SidebarLink({
@@ -40,6 +41,7 @@ export function SidebarLink({
4041
isExpanded,
4142
hideArrow,
4243
isPending,
44+
onToggle,
4345
}: SidebarLinkProps) {
4446
const ref = useRef<HTMLAnchorElement>(null);
4547

@@ -115,7 +117,24 @@ export function SidebarLink({
115117
className={cn('pe-1', {
116118
'text-link dark:text-link-dark': isExpanded,
117119
'text-tertiary dark:text-tertiary-dark': !isExpanded,
118-
})}>
120+
})}
121+
role="button"
122+
tabIndex={0}
123+
onClick={(e) => {
124+
if (onToggle) {
125+
e.preventDefault();
126+
onToggle();
127+
}
128+
}}
129+
onKeyDown={(e) => {
130+
if (onToggle && (e.key === 'Enter' || e.key === ' ')) {
131+
e.preventDefault();
132+
onToggle();
133+
}
134+
}}
135+
aria-label={isExpanded ? "Collapse" : "Expand"}
136+
aria-expanded={isExpanded}
137+
>
119138
<IconNavArrow displayDirection={isExpanded ? 'down' : 'end'} />
120139
</span>
121140
)}

src/components/Layout/Sidebar/SidebarRouteTree.tsx

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Copyright (c) Facebook, Inc. and its affiliates.
1010
*/
1111

12-
import {useRef, useLayoutEffect, Fragment} from 'react';
12+
import {useRef, useLayoutEffect, Fragment, useState, useEffect} from 'react';
1313

1414
import cn from 'classnames';
1515
import {useRouter} from 'next/router';
@@ -78,6 +78,67 @@ function CollapseWrapper({
7878
);
7979
}
8080

81+
interface ExpandableSidebarItemProps {
82+
title: string;
83+
path: string;
84+
level: number;
85+
routes?: RouteItem[];
86+
version?: 'canary' | 'major' | 'experimental' | 'rc';
87+
isForceExpanded: boolean;
88+
breadcrumbs: RouteItem[];
89+
selected: boolean;
90+
pendingRoute: string | null;
91+
}
92+
93+
function ExpandableSidebarItem({
94+
title,
95+
path,
96+
level,
97+
routes,
98+
version,
99+
isForceExpanded,
100+
breadcrumbs,
101+
selected,
102+
pendingRoute,
103+
}: ExpandableSidebarItemProps) {
104+
const isBreadcrumb =
105+
breadcrumbs.length > 1 &&
106+
breadcrumbs[breadcrumbs.length - 1].path === path;
107+
const defaultExpanded = isForceExpanded || isBreadcrumb || selected;
108+
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
109+
110+
useEffect(() => {
111+
if (defaultExpanded) {
112+
setIsExpanded(true);
113+
}
114+
}, [defaultExpanded]);
115+
116+
return (
117+
<li key={`${title}-${path}-${level}-heading`}>
118+
<SidebarLink
119+
key={`${title}-${path}-${level}-link`}
120+
href={path}
121+
isPending={pendingRoute === path}
122+
selected={selected}
123+
level={level}
124+
title={title}
125+
version={version}
126+
isExpanded={isExpanded}
127+
hideArrow={isForceExpanded}
128+
onToggle={() => setIsExpanded(!isExpanded)}
129+
/>
130+
<CollapseWrapper duration={250} isExpanded={isExpanded}>
131+
<SidebarRouteTree
132+
isForceExpanded={isForceExpanded}
133+
routeTree={{title, routes}}
134+
breadcrumbs={breadcrumbs}
135+
level={level + 1}
136+
/>
137+
</CollapseWrapper>
138+
</li>
139+
);
140+
}
141+
81142
export function SidebarRouteTree({
82143
isForceExpanded,
83144
breadcrumbs,
@@ -116,32 +177,19 @@ export function SidebarRouteTree({
116177
);
117178
} else if (routes) {
118179
// if route has a path and child routes, treat it as an expandable sidebar item
119-
const isBreadcrumb =
120-
breadcrumbs.length > 1 &&
121-
breadcrumbs[breadcrumbs.length - 1].path === path;
122-
const isExpanded = isForceExpanded || isBreadcrumb || selected;
123180
listItem = (
124-
<li key={`${title}-${path}-${level}-heading`}>
125-
<SidebarLink
126-
key={`${title}-${path}-${level}-link`}
127-
href={path}
128-
isPending={pendingRoute === path}
129-
selected={selected}
130-
level={level}
131-
title={title}
132-
version={version}
133-
isExpanded={isExpanded}
134-
hideArrow={isForceExpanded}
135-
/>
136-
<CollapseWrapper duration={250} isExpanded={isExpanded}>
137-
<SidebarRouteTree
138-
isForceExpanded={isForceExpanded}
139-
routeTree={{title, routes}}
140-
breadcrumbs={breadcrumbs}
141-
level={level + 1}
142-
/>
143-
</CollapseWrapper>
144-
</li>
181+
<ExpandableSidebarItem
182+
key={`${title}-${path}-${level}-heading`}
183+
title={title}
184+
path={path}
185+
level={level}
186+
routes={routes}
187+
version={version}
188+
isForceExpanded={isForceExpanded}
189+
breadcrumbs={breadcrumbs}
190+
selected={selected}
191+
pendingRoute={pendingRoute}
192+
/>
145193
);
146194
} else {
147195
// if route has a path and no child routes, treat it as a sidebar link

0 commit comments

Comments
 (0)