Skip to content

Commit d151ae0

Browse files
jubradclaude
andcommitted
console: pin the Admin nav group to the bottom of the side panel
The Admin group sat directly below the region-scoped nav items, so whenever those items were hidden (no enabled environment, or a blocked organization) it slid up to the top of the panel. Pin it to the bottom instead, so account-scoped links keep a stable position regardless of environment state. Also hide the Create New button while no environment is ready. All of its actions are disabled in that state, so it only added noise to the pared-down panel. Adds NavBar tests for the Admin group ordering and for the pared-down no-environment state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a626497 commit d151ae0

3 files changed

Lines changed: 92 additions & 11 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright Materialize, Inc. and contributors. All rights reserved.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the LICENSE file.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0.
9+
10+
import { screen } from "@testing-library/react";
11+
import React from "react";
12+
13+
import server from "~/api/mocks/server";
14+
import {
15+
disabledEnvironment,
16+
healthyEnvironment,
17+
renderComponent,
18+
setFakeEnvironment,
19+
} from "~/test/utils";
20+
21+
import { NavBar } from "./NavBar";
22+
23+
describe("NavBar", () => {
24+
afterEach(() => {
25+
server.resetHandlers();
26+
vi.clearAllMocks();
27+
});
28+
29+
it("renders the Admin group after the region-scoped items", async () => {
30+
await renderComponent(<NavBar isCollapsed={false} />, {
31+
initializeState: ({ set }) =>
32+
setFakeEnvironment(set, "aws/us-east-1", healthyEnvironment),
33+
});
34+
const clusters = await screen.findByText("Clusters");
35+
const admin = screen.getByText("Admin");
36+
// Admin is pinned to the bottom of the menu, so it must come after the
37+
// region-scoped items in document order.
38+
expect(
39+
clusters.compareDocumentPosition(admin) &
40+
Node.DOCUMENT_POSITION_FOLLOWING,
41+
).toBeTruthy();
42+
expect(screen.getByText("Create New")).toBeInTheDocument();
43+
});
44+
45+
it("hides the create button and region items without an enabled environment", async () => {
46+
await renderComponent(<NavBar isCollapsed={false} />, {
47+
initializeState: ({ set }) =>
48+
setFakeEnvironment(set, "aws/us-east-1", disabledEnvironment),
49+
});
50+
expect(await screen.findByText("Admin")).toBeInTheDocument();
51+
expect(screen.queryByText("Create New")).not.toBeInTheDocument();
52+
expect(screen.queryByText("Clusters")).not.toBeInTheDocument();
53+
expect(screen.queryByText("SQL Shell")).not.toBeInTheDocument();
54+
});
55+
});

console/src/layouts/NavBar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,9 @@ export const NavBar = ({ isCollapsed }: NavBarProps) => {
236236
/>
237237
</Box>
238238
)}
239-
<CreateObjectButton isCollapsed={isCollapsed} />
239+
<HideIfEnvironmentDisabled>
240+
<CreateObjectButton isCollapsed={isCollapsed} />
241+
</HideIfEnvironmentDisabled>
240242
<AppConfigSwitch
241243
cloudConfigElement={({ runtimeConfig }) => (
242244
<CloudNavMenu
@@ -249,7 +251,6 @@ export const NavBar = ({ isCollapsed }: NavBarProps) => {
249251
<SelfManagedNavMenu isCollapsed={isCollapsed} isMobile={false} />
250252
}
251253
/>
252-
{!isMobile && <Spacer />}
253254
{!isMobile && !isCollapsed && (
254255
<FreeTrialNotice mb="4" mx={{ lg: "4" }} />
255256
)}

console/src/layouts/NavBar/NavMenu.tsx

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export type NavItemType = {
5050
navItems?: NavItemType[];
5151
onClick?: () => void;
5252
forceShow?: boolean;
53+
/** Renders the item at the bottom of the nav menu, below a spacer. */
54+
pinToBottom?: boolean;
5355
};
5456

5557
const getNavItems = ({
@@ -144,6 +146,7 @@ const getNavItems = ({
144146
icon: <AdminIcon />,
145147
label: "Admin",
146148
forceShow: true,
149+
pinToBottom: true,
147150
navItems: [
148151
...(canViewAppPasswords
149152
? [
@@ -253,9 +256,17 @@ const useSelfManagedNavMenuItems = () => {
253256
};
254257

255258
const NavMenu = (props: { isCollapsed?: boolean; items: NavItemType[] }) => {
259+
const topItems = props.items.filter((item) => !item.pinToBottom);
260+
const bottomItems = props.items.filter((item) => item.pinToBottom);
256261
return (
257262
<NavMenuContainer>
258-
{props.items.map((item) => (
263+
{topItems.map((item) => (
264+
<HideIfEnvironmentDisabled key={item.label} forceShow={item.forceShow}>
265+
<NavItem key={item.label} {...item} isCollapsed={props.isCollapsed} />
266+
</HideIfEnvironmentDisabled>
267+
))}
268+
<Spacer />
269+
{bottomItems.map((item) => (
259270
<HideIfEnvironmentDisabled key={item.label} forceShow={item.forceShow}>
260271
<NavItem key={item.label} {...item} isCollapsed={props.isCollapsed} />
261272
</HideIfEnvironmentDisabled>
@@ -285,16 +296,30 @@ const NavMenuMobile = (props: {
285296
overflowY="auto"
286297
>
287298
<VStack px="4" py="6">
288-
{props.items.map((item) => (
289-
<HideIfEnvironmentDisabled
290-
key={item.label}
291-
forceShow={item.forceShow}
292-
>
293-
<NavItem key={item.label} closeMenu={props.closeMenu} {...item} />
294-
</HideIfEnvironmentDisabled>
295-
))}
299+
{props.items
300+
.filter((item) => !item.pinToBottom)
301+
.map((item) => (
302+
<HideIfEnvironmentDisabled
303+
key={item.label}
304+
forceShow={item.forceShow}
305+
>
306+
<NavItem key={item.label} closeMenu={props.closeMenu} {...item} />
307+
</HideIfEnvironmentDisabled>
308+
))}
296309
</VStack>
297310
<Spacer />
311+
<VStack px="4" pb="2">
312+
{props.items
313+
.filter((item) => item.pinToBottom)
314+
.map((item) => (
315+
<HideIfEnvironmentDisabled
316+
key={item.label}
317+
forceShow={item.forceShow}
318+
>
319+
<NavItem key={item.label} closeMenu={props.closeMenu} {...item} />
320+
</HideIfEnvironmentDisabled>
321+
))}
322+
</VStack>
298323
<VStack align="stretch">
299324
<FreeTrialNotice />
300325
<VStack>

0 commit comments

Comments
 (0)