Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion apps/admin/src/layout/app-sidebar/nav-content.helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,27 @@ describe('isMembersNavActive', () => {
membersForwardEnabled: false,
isOnMembersForward: false,
hasActiveMemberView: false,
isMembersExpanded: false,
isLegacyMembersRouteActive: true
})).toBe(true);
});

it('marks the base Members link inactive when a saved member view is active', () => {
it('marks the base Members link active when a saved member view is active but collapsed', () => {
expect(isMembersNavActive({
membersForwardEnabled: true,
isOnMembersForward: true,
hasActiveMemberView: true,
isMembersExpanded: false,
isLegacyMembersRouteActive: false
})).toBe(true);
});

it('marks the base Members link inactive when a saved member view is active and expanded', () => {
expect(isMembersNavActive({
membersForwardEnabled: true,
isOnMembersForward: true,
hasActiveMemberView: true,
isMembersExpanded: true,
isLegacyMembersRouteActive: false
})).toBe(false);
});
Expand All @@ -36,6 +48,7 @@ describe('isMembersNavActive', () => {
membersForwardEnabled: true,
isOnMembersForward: true,
hasActiveMemberView: false,
isMembersExpanded: false,
isLegacyMembersRouteActive: false
})).toBe(true);
});
Expand Down
8 changes: 7 additions & 1 deletion apps/admin/src/layout/app-sidebar/nav-content.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,25 @@ export function isMembersNavActive({
membersForwardEnabled,
isOnMembersForward,
hasActiveMemberView,
isMembersExpanded,
isLegacyMembersRouteActive
}: {
membersForwardEnabled: boolean;
isOnMembersForward: boolean;
hasActiveMemberView: boolean;
isMembersExpanded: boolean;
isLegacyMembersRouteActive: boolean;
}): boolean {
if (!membersForwardEnabled) {
return isLegacyMembersRouteActive;
}

if (isOnMembersForward) {
return !hasActiveMemberView;
if (!hasActiveMemberView) {
return true;
}

return !isMembersExpanded;
}

return isLegacyMembersRouteActive;
Expand Down
232 changes: 131 additions & 101 deletions apps/admin/src/layout/app-sidebar/nav-content.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from "react"

import {
Button,
formatNumber,
LucideIcon,
SidebarGroup,
Expand All @@ -13,20 +12,71 @@ import { useLocation } from "@tryghost/admin-x-framework";
import { useCurrentUser } from "@tryghost/admin-x-framework/api/current-user";
import { canManageMembers, canManageTags } from "@tryghost/admin-x-framework/api/users";
import { NavMenuItem } from "./nav-menu-item";
import NavSubMenu from "./nav-sub-menu";
import { useMemberCount } from "./hooks/use-member-count";
import { useNavigationExpanded } from "./hooks/use-navigation-preferences";
import { NavCustomViews } from "./nav-custom-views";
import { NavMemberViews } from "./nav-member-views";
import { useMemberSidebarViews } from "./member-sidebar-views";
import { getMembersNavActiveRoutes, isMembersNavActive } from "./nav-content.helpers";
import { useCustomSidebarViews } from "./use-custom-sidebar-views";
import { useEmberRouting } from "@/ember-bridge";
import { useFeatureFlag } from "@/hooks/use-feature-flag";

function PostsNavItemContent({isActive, to}: {isActive: boolean; to: string}) {
return (
<>
<NavMenuItem.Link
to={to}
isActive={isActive}
>
<LucideIcon.PenLine className="opacity-0 sidebar:opacity-100 sidebar:group-hover/menu-item:opacity-0 pointer-events-none transition-all" />
<NavMenuItem.Label>Posts</NavMenuItem.Label>
</NavMenuItem.Link>
<a href="#/editor/post"
aria-label="Create new post"
className="flex items-center justify-center absolute hover:bg-sidebar-accent transition-all rounded-full right-0 top-0 p-0 size-8 text-gray-700 hover:text-sidebar-accent-foreground"
>
<LucideIcon.Plus
size={20}
className="stroke-[1.5px]! mt-px"
/>
</a>
</>
);
}

function MembersNavItemContent({
collapsible,
count,
isActive,
to
}: {
collapsible: boolean;
count: number | null | undefined;
isActive: boolean;
to: string;
}) {
return (
<>
<NavMenuItem.Link
to={to}
isActive={isActive}
>
<LucideIcon.Users className={collapsible ? "opacity-0 sidebar:opacity-100 sidebar:group-hover/menu-item:opacity-0 pointer-events-none transition-all" : ""} />
<NavMenuItem.Label>Members</NavMenuItem.Label>
</NavMenuItem.Link>
{count != null && (
<SidebarMenuBadge>{(formatNumber as (value: number) => string)(count)}</SidebarMenuBadge>
)}
</>
);
}

function NavContent({ ...props }: React.ComponentProps<typeof SidebarGroup>) {
const { data: currentUser } = useCurrentUser();
const [postsExpanded, setPostsExpanded] = useNavigationExpanded('posts');
const [savedPostsExpanded, setPostsExpanded] = useNavigationExpanded('posts');
const [savedMembersExpanded, setMembersExpanded] = useNavigationExpanded('members');
const postCustomViews = useCustomSidebarViews('posts');
const memberViews = useMemberSidebarViews();
const hasMemberViews = memberViews.length > 0;
const location = useLocation();
Expand All @@ -37,89 +87,76 @@ function NavContent({ ...props }: React.ComponentProps<typeof SidebarGroup>) {

const showTags = currentUser && canManageTags(currentUser);
const showMembers = currentUser && canManageMembers(currentUser);
const isDraftPostsRouteActive = routing.isRouteActive('posts', {type: 'draft'});
const isScheduledPostsRouteActive = routing.isRouteActive('posts', {type: 'scheduled'});
const isPublishedPostsRouteActive = routing.isRouteActive('posts', {type: 'published'});
const hasActivePostChild = isDraftPostsRouteActive || isScheduledPostsRouteActive || isPublishedPostsRouteActive || postCustomViews.some(view => view.isActive);
const postsExpanded = savedPostsExpanded;
const isOnMembersForward = location.pathname === '/members-forward';
const hasActiveMemberView = isOnMembersForward && memberViews.some(view => view.isActive);
const membersExpanded = savedMembersExpanded || hasActiveMemberView;
const membersExpanded = savedMembersExpanded;
const membersNavActive = isMembersNavActive({
membersForwardEnabled,
isOnMembersForward,
hasActiveMemberView,
isMembersExpanded: membersExpanded,
isLegacyMembersRouteActive: routing.isRouteActive(getMembersNavActiveRoutes())
});
const postsRoute = routing.getRouteUrl('posts');
const isPostsRouteActive = routing.isRouteActive('posts');
const postsNavActive = isPostsRouteActive || (!postsExpanded && hasActivePostChild);
const membersRoute = membersForwardEnabled ? 'members-forward' : routing.getRouteUrl('members');

return (
<SidebarGroup {...props}>
<SidebarGroupContent>
<SidebarMenu>
<NavMenuItem>
<Button
aria-controls="posts-submenu"
aria-expanded={postsExpanded}
aria-label="Toggle post views"
variant="ghost"
size="icon"
className="h-[34px]! absolute sidebar:opacity-0 group-hover/menu-item:opacity-100 focus-visible:opacity-100 transition-all left-3 top-0 p-0 h-9 w-auto text-sidebar-accent-foreground hover:text-gray-black hover:bg-transparent"
onClick={() =>
void setPostsExpanded(!postsExpanded)
}
>
<LucideIcon.ChevronRight
size={16}
className={`transition-all ${postsExpanded && 'rotate-[90deg]'}`}
/>
</Button>
<NavMenuItem.Link
to={routing.getRouteUrl('posts')}
isActive={routing.isRouteActive('posts')}
>
<LucideIcon.PenLine className="opacity-0 sidebar:opacity-100 sidebar:group-hover/menu-item:opacity-0 pointer-events-none transition-all" />
<NavMenuItem.Label>Posts</NavMenuItem.Label>
</NavMenuItem.Link>
<a href="#/editor/post"
aria-label="Create new post"
className="flex items-center justify-center absolute hover:bg-sidebar-accent transition-all rounded-full right-0 top-0 p-0 size-8 text-gray-700 hover:text-sidebar-accent-foreground"
>
<LucideIcon.Plus
size={20}
className="stroke-[1.5px]! mt-px"
<NavMenuItem.Collapsible
expanded={postsExpanded}
id="posts-submenu"
onExpandedChange={setPostsExpanded}
>
<NavMenuItem.CollapsibleItem ariaLabel="Toggle post views">
<PostsNavItemContent
isActive={postsNavActive}
to={postsRoute}
/>
</a>
</NavMenuItem>
</NavMenuItem.CollapsibleItem>

{/* Posts submenu */}
<NavSubMenu isExpanded={postsExpanded} id="posts-submenu">
<NavMenuItem>
<NavMenuItem.Link
className="pl-9"
to="posts?type=draft"
isActive={routing.isRouteActive('posts', {type: 'draft'})}
>
<NavMenuItem.Label>Drafts</NavMenuItem.Label>
</NavMenuItem.Link>
</NavMenuItem>
<NavMenuItem.CollapsibleMenu>
<NavMenuItem>
<NavMenuItem.Link
className="pl-9"
to="posts?type=draft"
isActive={isDraftPostsRouteActive}
>
<NavMenuItem.Label>Drafts</NavMenuItem.Label>
</NavMenuItem.Link>
</NavMenuItem>

<NavMenuItem>
<NavMenuItem.Link
className="pl-9"
to="posts?type=scheduled"
isActive={routing.isRouteActive('posts', {type: 'scheduled'})}
>
<NavMenuItem.Label>Scheduled</NavMenuItem.Label>
</NavMenuItem.Link>
</NavMenuItem>
<NavMenuItem>
<NavMenuItem.Link
className="pl-9"
to="posts?type=scheduled"
isActive={isScheduledPostsRouteActive}
>
<NavMenuItem.Label>Scheduled</NavMenuItem.Label>
</NavMenuItem.Link>
</NavMenuItem>

<NavMenuItem>
<NavMenuItem.Link
className="pl-9"
to="posts?type=published"
isActive={routing.isRouteActive('posts', {type: 'published'})}
>
<NavMenuItem.Label>Published</NavMenuItem.Label>
</NavMenuItem.Link>
</NavMenuItem>
<NavMenuItem>
<NavMenuItem.Link
className="pl-9"
to="posts?type=published"
isActive={isPublishedPostsRouteActive}
>
<NavMenuItem.Label>Published</NavMenuItem.Label>
</NavMenuItem.Link>
</NavMenuItem>

<NavCustomViews />
</NavSubMenu>
<NavCustomViews />
</NavMenuItem.CollapsibleMenu>
</NavMenuItem.Collapsible>

<NavMenuItem>
<NavMenuItem.Link
Expand All @@ -145,41 +182,34 @@ function NavContent({ ...props }: React.ComponentProps<typeof SidebarGroup>) {

{showMembers && (
<>
<NavMenuItem>
{membersForwardEnabled && hasMemberViews && (
<Button
aria-controls="members-submenu"
aria-expanded={membersExpanded}
aria-label="Toggle member views"
variant="ghost"
size="icon"
className="h-[34px]! absolute sidebar:opacity-0 group-hover/menu-item:opacity-100 focus-visible:opacity-100 transition-all left-3 top-0 p-0 h-9 w-auto text-sidebar-accent-foreground hover:text-gray-black hover:bg-transparent"
onClick={() =>
void setMembersExpanded(!membersExpanded)
}
>
<LucideIcon.ChevronRight
size={16}
className={`transition-all ${membersExpanded && 'rotate-[90deg]'}`}
/>
</Button>
)}
<NavMenuItem.Link
to={membersForwardEnabled ? 'members-forward' : routing.getRouteUrl('members')}
isActive={membersNavActive}
{membersForwardEnabled && hasMemberViews ? (
<NavMenuItem.Collapsible
expanded={membersExpanded}
id="members-submenu"
onExpandedChange={setMembersExpanded}
>
<LucideIcon.Users className={membersForwardEnabled && hasMemberViews ? "opacity-0 sidebar:opacity-100 sidebar:group-hover/menu-item:opacity-0 pointer-events-none transition-all" : ""} />
<NavMenuItem.Label>Members</NavMenuItem.Label>
</NavMenuItem.Link>
{memberCount != null && (
<SidebarMenuBadge>{(formatNumber as (value: number) => string)(memberCount)}</SidebarMenuBadge>
)}
</NavMenuItem>
<NavMenuItem.CollapsibleItem ariaLabel="Toggle member views">
<MembersNavItemContent
collapsible={true}
count={memberCount}
isActive={membersNavActive}
to={membersRoute}
/>
</NavMenuItem.CollapsibleItem>

{membersForwardEnabled && hasMemberViews && (
<NavSubMenu id="members-submenu" isExpanded={membersExpanded}>
<NavMemberViews />
</NavSubMenu>
<NavMenuItem.CollapsibleMenu>
<NavMemberViews />
</NavMenuItem.CollapsibleMenu>
</NavMenuItem.Collapsible>
) : (
<NavMenuItem>
<MembersNavItemContent
collapsible={false}
count={memberCount}
isActive={membersNavActive}
to={membersRoute}
/>
</NavMenuItem>
)}
</>
)}
Expand Down
Loading
Loading