chore(sidebar): add common content and clarify navigation
This commit is contained in:
parent
de843f658a
commit
ee3a0a2bba
7 changed files with 189 additions and 34 deletions
|
|
@ -49,6 +49,7 @@ import { State } from "@/types/proto/api/v1/common_pb";
|
|||
import { User_Role, UserNotification_Status } from "@/types/proto/api/v1/user_service_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import MemosLogo from "../MemosLogo";
|
||||
import CommonSidebarContent from "./CommonSidebarContent";
|
||||
import { getSidebarRouteKind, routeSupportsCollectionScope } from "./routes";
|
||||
import SidebarRow, { SIDEBAR_ROW_CLASSES, SIDEBAR_ROW_FOCUS_CLASSES, SidebarRowIconSlot, sidebarRowStateClasses } from "./SidebarRow";
|
||||
import SidebarSection, { SIDEBAR_SECTION_STACK_CLASSES } from "./SidebarSection";
|
||||
|
|
@ -289,6 +290,7 @@ const RouteSidebarContent = () => {
|
|||
if (kind === "inbox") return <InboxSidebarContent />;
|
||||
if (kind === "settings") return <SettingsSidebarContent />;
|
||||
if (kind === "memo") return <MemoDetailSidebarContent />;
|
||||
if (kind === "common") return <CommonSidebarContent />;
|
||||
return null;
|
||||
};
|
||||
|
||||
|
|
@ -299,7 +301,6 @@ interface GlobalNavItem {
|
|||
icon: LucideIcon;
|
||||
active: boolean;
|
||||
count?: number;
|
||||
alwaysExpanded?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -390,7 +391,6 @@ const GlobalNavigation = () => {
|
|||
path: ROUTES.EXPLORE,
|
||||
icon: EarthIcon,
|
||||
active: routeKind === "explore" || routeKind === "profile" || routeKind === "memo",
|
||||
alwaysExpanded: true,
|
||||
},
|
||||
{
|
||||
id: "about",
|
||||
|
|
@ -401,6 +401,13 @@ const GlobalNavigation = () => {
|
|||
},
|
||||
];
|
||||
|
||||
// Keep one textual anchor in the compact navigator. A real active destination
|
||||
// takes precedence; routes outside this navigation default to its first item
|
||||
// without presenting that fallback as the current page.
|
||||
const activeNavigatorItemId = currentUser && scopeRouteActive ? "scope" : items.find((item) => item.active)?.id;
|
||||
const expandedNavigatorItemId = activeNavigatorItemId ?? (currentUser ? "scope" : items[0]?.id);
|
||||
const scopeExpanded = expandedNavigatorItemId === "scope";
|
||||
|
||||
const scopeMenuContent = (
|
||||
<DropdownMenuContent align="start" sideOffset={4} className="flex w-36 flex-col gap-0.5">
|
||||
{scopeItems.map((item) => {
|
||||
|
|
@ -437,7 +444,7 @@ const GlobalNavigation = () => {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Tooltip disabled={scopeRouteActive}>
|
||||
<Tooltip disabled={scopeExpanded}>
|
||||
{/* The tooltip anchors to a wrapper span rather than the button: a disabled
|
||||
tooltip stamps data-trigger-disabled on its trigger element, and Base UI's
|
||||
shared floating logic would read that as the MENU trigger being disabled. */}
|
||||
|
|
@ -453,11 +460,13 @@ const GlobalNavigation = () => {
|
|||
}
|
||||
>
|
||||
<ActiveScopeIcon className="size-4 shrink-0" strokeWidth={1.8} />
|
||||
<NavPillLabel expanded={scopeRouteActive} label={activeScopeItem.label}>
|
||||
<ChevronDownIcon
|
||||
className="-mr-0.5 size-3 shrink-0 opacity-55 transition-transform duration-200 ease-out group-data-[popup-open]/scope:rotate-180 motion-reduce:transition-none"
|
||||
strokeWidth={1.8}
|
||||
/>
|
||||
<NavPillLabel expanded={scopeExpanded} label={activeScopeItem.label}>
|
||||
{scopeRouteActive && (
|
||||
<ChevronDownIcon
|
||||
className="-mr-0.5 size-3 shrink-0 opacity-55 transition-transform duration-200 ease-out group-data-[popup-open]/scope:rotate-180 motion-reduce:transition-none"
|
||||
strokeWidth={1.8}
|
||||
/>
|
||||
)}
|
||||
</NavPillLabel>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
|
|
@ -468,7 +477,7 @@ const GlobalNavigation = () => {
|
|||
)}
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const expanded = item.active || !!item.alwaysExpanded;
|
||||
const expanded = item.id === expandedNavigatorItemId;
|
||||
return (
|
||||
<Tooltip key={item.id} disabled={expanded}>
|
||||
<TooltipTrigger
|
||||
|
|
|
|||
71
web/src/components/AppSidebar/CommonSidebarContent.tsx
Normal file
71
web/src/components/AppSidebar/CommonSidebarContent.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { BookOpenIcon, BracesIcon, ExternalLinkIcon, GitForkIcon, InfoIcon, type LucideIcon } from "lucide-react";
|
||||
import { Link, matchPath, useLocation } from "react-router-dom";
|
||||
import { useAppSidebar } from "@/contexts/AppSidebarContext";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { MEMOS_API_DOCUMENTATION_URL, MEMOS_DOCUMENTATION_URL, MEMOS_GITHUB_URL } from "@/lib/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { SIDEBAR_ROW_CLASSES, SidebarRowIconSlot, sidebarRowStateClasses } from "./SidebarRow";
|
||||
import SidebarSection, { SIDEBAR_SECTION_STACK_CLASSES } from "./SidebarSection";
|
||||
|
||||
interface ResourceLink {
|
||||
labelKey: "about.documents" | "about.api-docs" | "about.github-repository";
|
||||
href: string;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
const RESOURCE_LINKS: ResourceLink[] = [
|
||||
{ labelKey: "about.documents", href: MEMOS_DOCUMENTATION_URL, icon: BookOpenIcon },
|
||||
{ labelKey: "about.api-docs", href: MEMOS_API_DOCUMENTATION_URL, icon: BracesIcon },
|
||||
{ labelKey: "about.github-repository", href: MEMOS_GITHUB_URL, icon: GitForkIcon },
|
||||
];
|
||||
|
||||
const CommonSidebarContent = () => {
|
||||
const t = useTranslate();
|
||||
const location = useLocation();
|
||||
const currentUser = useCurrentUser();
|
||||
const { setMobileOpen } = useAppSidebar();
|
||||
const aboutActive = Boolean(matchPath(ROUTES.ABOUT, location.pathname));
|
||||
|
||||
return (
|
||||
<div className={SIDEBAR_SECTION_STACK_CLASSES}>
|
||||
{currentUser && (
|
||||
<SidebarSection ariaLabel={t("common.about")}>
|
||||
<Link
|
||||
to={ROUTES.ABOUT}
|
||||
aria-current={aboutActive ? "page" : undefined}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className={cn(SIDEBAR_ROW_CLASSES, sidebarRowStateClasses(aboutActive))}
|
||||
>
|
||||
<SidebarRowIconSlot icon={InfoIcon} />
|
||||
<span className="min-w-0 flex-1 truncate">{t("common.about")}</span>
|
||||
</Link>
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
<SidebarSection label={t("common.resources")}>
|
||||
{RESOURCE_LINKS.map((resource) => (
|
||||
<a
|
||||
key={resource.href}
|
||||
href={resource.href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className={cn(SIDEBAR_ROW_CLASSES, sidebarRowStateClasses())}
|
||||
>
|
||||
<SidebarRowIconSlot icon={resource.icon} />
|
||||
<span className="min-w-0 flex-1 truncate">{t(resource.labelKey)}</span>
|
||||
<ExternalLinkIcon
|
||||
aria-hidden="true"
|
||||
className="size-3 shrink-0 text-muted-foreground/50 transition-colors group-hover:text-foreground/70"
|
||||
strokeWidth={1.8}
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
</SidebarSection>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommonSidebarContent;
|
||||
|
|
@ -2,7 +2,7 @@ import { matchPath } from "react-router-dom";
|
|||
import { isMemoScopeRoute, type MemoScope, resolveMemoScope } from "@/lib/memo-views";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
|
||||
export type SidebarRouteKind = MemoScope | "profile" | "views" | "attachments" | "inbox" | "settings" | "memo" | "empty";
|
||||
export type SidebarRouteKind = MemoScope | "profile" | "views" | "attachments" | "inbox" | "settings" | "memo" | "common";
|
||||
|
||||
export type RouteSearchScope = "remembered-collection" | "user-collection" | "profile" | "all";
|
||||
export type RouteComposePlacement = "remembered-space" | "unassigned";
|
||||
|
|
@ -23,7 +23,7 @@ export const getSidebarRouteKind = (path: string): SidebarRouteKind => {
|
|||
if (matchPath(ROUTES.INBOX, normalizedPath)) return "inbox";
|
||||
if (matchPath(ROUTES.SETTING, normalizedPath)) return "settings";
|
||||
if (matchPath("/memos/:uid", normalizedPath) || matchPath(`${ROUTES.SHARED_MEMO}/:token`, normalizedPath)) return "memo";
|
||||
return "empty";
|
||||
return "common";
|
||||
};
|
||||
|
||||
/** Routes whose collections are filtered by the remembered All / Space scope. */
|
||||
|
|
|
|||
|
|
@ -8,5 +8,10 @@ export const DEFAULT_LIST_MEMOS_PAGE_SIZE = 16;
|
|||
// Loads that finish faster than this never render the spinner, avoiding a flash on fast/self-hosted networks.
|
||||
export const LOADING_INDICATOR_DELAY_MS = 250;
|
||||
|
||||
export const MEMOS_WEBSITE_URL = "https://usememos.com/";
|
||||
export const MEMOS_DOCUMENTATION_URL = "https://usememos.com/docs";
|
||||
export const MEMOS_API_DOCUMENTATION_URL = "https://usememos.com/docs/api";
|
||||
export const MEMOS_GITHUB_URL = "https://github.com/usememos/memos";
|
||||
|
||||
// Official companion browser extension for saving web content to Memos.
|
||||
export const WEB_CLIPPER_URL = "https://github.com/usememos/web-clipper";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import { ExternalLinkIcon, ScissorsIcon } from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useInstance } from "@/contexts/InstanceContext";
|
||||
import { WEB_CLIPPER_URL } from "@/lib/constants";
|
||||
import {
|
||||
MEMOS_API_DOCUMENTATION_URL,
|
||||
MEMOS_DOCUMENTATION_URL,
|
||||
MEMOS_GITHUB_URL,
|
||||
MEMOS_WEBSITE_URL,
|
||||
WEB_CLIPPER_URL,
|
||||
} from "@/lib/constants";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const GITHUB_COMMIT_URL_PREFIX = "https://github.com/usememos/memos/commit/";
|
||||
|
|
@ -63,13 +69,13 @@ const About = () => {
|
|||
}
|
||||
|
||||
const projectLinks = [
|
||||
{ label: t("about.official-website"), note: t("about.official-website-note"), href: "https://usememos.com/" },
|
||||
{ label: t("about.documents"), note: t("about.documents-note"), href: "https://usememos.com/docs" },
|
||||
{ label: t("about.api-docs"), note: t("about.api-docs-note"), href: "https://usememos.com/docs/api" },
|
||||
{ label: t("about.official-website"), note: t("about.official-website-note"), href: MEMOS_WEBSITE_URL },
|
||||
{ label: t("about.documents"), note: t("about.documents-note"), href: MEMOS_DOCUMENTATION_URL },
|
||||
{ label: t("about.api-docs"), note: t("about.api-docs-note"), href: MEMOS_API_DOCUMENTATION_URL },
|
||||
{
|
||||
label: t("about.github-repository"),
|
||||
note: t("about.github-repository-note"),
|
||||
href: "https://github.com/usememos/memos",
|
||||
href: MEMOS_GITHUB_URL,
|
||||
},
|
||||
{ label: t("about.web-clipper"), note: t("about.web-clipper-platforms"), href: WEB_CLIPPER_URL, icon: ScissorsIcon },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { fireEvent, screen, render as testingLibraryRender } from "@testing-library/react";
|
||||
import { fireEvent, screen, render as testingLibraryRender, within } from "@testing-library/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import AppSidebar, { MobileAppHeader, MobileAppSidebar } from "@/components/AppSidebar";
|
||||
|
|
@ -13,6 +13,7 @@ const authState = vi.hoisted(() => ({
|
|||
const sidebarState = vi.hoisted(() => ({
|
||||
memoScope: "home" as "home" | "explore",
|
||||
mobileOpen: false,
|
||||
setMobileOpen: vi.fn(),
|
||||
}));
|
||||
const globalEditorState = vi.hoisted(() => ({
|
||||
canOpen: true,
|
||||
|
|
@ -70,7 +71,7 @@ vi.mock("@/contexts/AppSidebarContext", () => ({
|
|||
memoDetail: undefined,
|
||||
setMemoDetail: vi.fn(),
|
||||
mobileOpen: sidebarState.mobileOpen,
|
||||
setMobileOpen: vi.fn(),
|
||||
setMobileOpen: sidebarState.setMobileOpen,
|
||||
quickFindOpen: false,
|
||||
setQuickFindOpen: vi.fn(),
|
||||
memoScope: sidebarState.memoScope,
|
||||
|
|
@ -163,12 +164,24 @@ const expectCollapsedNavPill = (pill: HTMLElement, label: string) => {
|
|||
expect(labelTrack).toHaveTextContent(label);
|
||||
};
|
||||
|
||||
const expectActiveNavPill = (pill: HTMLElement, label: string) => {
|
||||
expect(pill).toHaveAttribute("aria-current", "page");
|
||||
expect(pill.querySelector('span[aria-hidden="true"]')).toBeNull();
|
||||
const expectExpandedNavPill = (pill: HTMLElement, label: string) => {
|
||||
expect(pill).toHaveClass("h-[30px]", "px-[7px]");
|
||||
const labelTrack = pill.querySelector("span.grid");
|
||||
expect(labelTrack).toHaveClass("grid-cols-[1fr]", "pl-2");
|
||||
expect(labelTrack).not.toHaveAttribute("aria-hidden");
|
||||
expect(pill).toHaveTextContent(label);
|
||||
};
|
||||
|
||||
const expectActiveNavPill = (pill: HTMLElement, label: string) => {
|
||||
expectExpandedNavPill(pill, label);
|
||||
expect(pill).toHaveAttribute("aria-current", "page");
|
||||
};
|
||||
|
||||
const expectDefaultNavPill = (pill: HTMLElement, label: string) => {
|
||||
expectExpandedNavPill(pill, label);
|
||||
expect(pill).not.toHaveAttribute("aria-current");
|
||||
};
|
||||
|
||||
describe("App sidebar logo", () => {
|
||||
beforeEach(() => {
|
||||
authState.currentUser = { name: "users/test" };
|
||||
|
|
@ -176,6 +189,7 @@ describe("App sidebar logo", () => {
|
|||
authState.notifications = [];
|
||||
sidebarState.memoScope = "home";
|
||||
sidebarState.mobileOpen = false;
|
||||
sidebarState.setMobileOpen.mockClear();
|
||||
globalEditorState.canOpen = true;
|
||||
globalEditorState.openEditor.mockClear();
|
||||
spaceState.spaces = [];
|
||||
|
|
@ -362,32 +376,66 @@ describe("App sidebar logo", () => {
|
|||
|
||||
expect(screen.getByRole("link", { name: "Memos logo" })).toHaveAttribute("href", "/explore");
|
||||
expect(screen.queryByRole("button", { name: /^space\.switch:/ })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("link", { name: "common.explore" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("link", { name: "common.about" })).toHaveAttribute("href", "/about");
|
||||
const navigation = within(screen.getByRole("navigation", { name: "Primary" }));
|
||||
expectActiveNavPill(navigation.getByRole("link", { name: "common.explore" }), "common.explore");
|
||||
const about = navigation.getByRole("link", { name: "common.about" });
|
||||
expect(about).toHaveAttribute("href", "/about");
|
||||
expectCollapsedNavPill(about, "common.about");
|
||||
expect(screen.getByRole("link", { name: "common.sign-in-to-memos" }).closest("footer")).not.toBeNull();
|
||||
expect(screen.queryByRole("button", { name: "editor.new-memo" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "common.home" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not inherit collection content on a route without a collection", () => {
|
||||
it.each(["/403", "/404", "/unknown"])("uses the common sidebar without inheriting collection content on %s", (path) => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/404"]}>
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
<AppSidebar />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("link", { name: "common.about" })).toHaveAttribute("href", "/about");
|
||||
expect(screen.getByRole("link", { name: "common.about" })).not.toHaveAttribute("aria-current");
|
||||
expect(screen.getByRole("heading", { name: "common.resources", level: 2 })).toBeInTheDocument();
|
||||
const documentationLink = screen.getByRole("link", { name: "about.documents" });
|
||||
expect(documentationLink).toHaveAttribute("href", "https://usememos.com/docs");
|
||||
expect(documentationLink).toHaveAttribute("target", "_blank");
|
||||
expect(documentationLink).toHaveAttribute("rel", "noreferrer");
|
||||
fireEvent.click(documentationLink);
|
||||
expect(sidebarState.setMobileOpen).toHaveBeenCalledWith(false);
|
||||
expect(screen.getByRole("link", { name: "about.api-docs" })).toHaveAttribute("href", "https://usememos.com/docs/api");
|
||||
expect(screen.getByRole("link", { name: "about.github-repository" })).toHaveAttribute("href", "https://github.com/usememos/memos");
|
||||
expect(screen.queryByText("Calendar")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("region", { name: "common.statistics" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("common.views")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Tags")).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("link", { name: "Memos logo" })).toHaveAttribute("href", "/");
|
||||
expect(screen.getByRole("link", { name: "common.attachments" })).toHaveAttribute("href", "/attachments");
|
||||
const navigation = within(screen.getByRole("navigation", { name: "Primary" }));
|
||||
expectDefaultNavPill(navigation.getByRole("button", { name: "common.home" }), "common.home");
|
||||
const attachments = navigation.getByRole("link", { name: "common.attachments" });
|
||||
expect(attachments).toHaveAttribute("href", "/attachments");
|
||||
expectCollapsedNavPill(attachments, "common.attachments");
|
||||
expect(screen.queryByRole("link", { name: "common.inbox" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "common.home" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "common.about" })).not.toBeInTheDocument();
|
||||
expect(screen.getByText("User menu").closest("footer")).not.toBeNull();
|
||||
});
|
||||
|
||||
it.each(["/about", "/About/"])("marks the common About link active on %s", (path) => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
<AppSidebar />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("link", { name: "common.about" })).toHaveAttribute("aria-current", "page");
|
||||
expect(screen.getByRole("heading", { name: "common.resources", level: 2 })).toBeInTheDocument();
|
||||
|
||||
const navigation = within(screen.getByRole("navigation", { name: "Primary" }));
|
||||
const scopeTrigger = navigation.getByRole("button", { name: "common.home" });
|
||||
expectDefaultNavPill(scopeTrigger, "common.home");
|
||||
expect(scopeTrigger.querySelector(".lucide-chevron-down")).not.toBeInTheDocument();
|
||||
expectCollapsedNavPill(navigation.getByRole("link", { name: "common.attachments" }), "common.attachments");
|
||||
});
|
||||
|
||||
it("uses a visitor sidebar for a guest on a route without contextual content", () => {
|
||||
authState.currentUser = undefined;
|
||||
render(
|
||||
|
|
@ -396,11 +444,16 @@ describe("App sidebar logo", () => {
|
|||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("link", { name: "common.explore" })).toHaveAttribute("href", "/explore");
|
||||
const navigation = within(screen.getByRole("navigation", { name: "Primary" }));
|
||||
const explore = navigation.getByRole("link", { name: "common.explore" });
|
||||
expect(explore).toHaveAttribute("href", "/explore");
|
||||
expectDefaultNavPill(explore, "common.explore");
|
||||
expect(screen.queryByRole("link", { name: "common.home" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "common.attachments" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "common.inbox" })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("link", { name: "common.about" })).toHaveAttribute("href", "/about");
|
||||
const about = navigation.getByRole("link", { name: "common.about" });
|
||||
expect(about).toHaveAttribute("href", "/about");
|
||||
expectCollapsedNavPill(about, "common.about");
|
||||
expect(screen.getByRole("link", { name: "common.sign-in-to-memos" }).closest("footer")).not.toBeNull();
|
||||
});
|
||||
|
||||
|
|
@ -412,7 +465,9 @@ describe("App sidebar logo", () => {
|
|||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("link", { name: "common.about" })).toHaveAttribute("aria-current", "page");
|
||||
const navigation = within(screen.getByRole("navigation", { name: "Primary" }));
|
||||
expectCollapsedNavPill(navigation.getByRole("link", { name: "common.explore" }), "common.explore");
|
||||
expectActiveNavPill(navigation.getByRole("link", { name: "common.about" }), "common.about");
|
||||
expect(screen.queryByText("Calendar")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
@ -439,6 +494,9 @@ describe("App sidebar logo", () => {
|
|||
expect(screen.queryByRole("button", { name: "common.all" })).not.toBeInTheDocument();
|
||||
|
||||
const scopeTrigger = screen.getByRole("button", { name: "common.home" });
|
||||
expectActiveNavPill(scopeTrigger, "common.home");
|
||||
expect(scopeTrigger.querySelector(".lucide-chevron-down")).toBeInTheDocument();
|
||||
expectCollapsedNavPill(screen.getByRole("link", { name: "common.attachments" }), "common.attachments");
|
||||
fireEvent.click(scopeTrigger);
|
||||
expect(await screen.findByRole("menuitem", { name: "common.home" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("menuitem", { name: "common.explore" })).toBeInTheDocument();
|
||||
|
|
@ -511,7 +569,9 @@ describe("App sidebar logo", () => {
|
|||
);
|
||||
|
||||
const scopeTrigger = screen.getByRole("button", { name: "common.explore" });
|
||||
expectCollapsedNavPill(scopeTrigger, "common.explore");
|
||||
expectDefaultNavPill(scopeTrigger, "common.explore");
|
||||
expect(scopeTrigger.querySelector(".lucide-chevron-down")).not.toBeInTheDocument();
|
||||
expectCollapsedNavPill(screen.getByRole("link", { name: "common.attachments" }), "common.attachments");
|
||||
|
||||
fireEvent.click(scopeTrigger);
|
||||
expectActiveNavPill(await screen.findByRole("button", { name: "common.explore", current: "page" }), "common.explore");
|
||||
|
|
@ -526,7 +586,9 @@ describe("App sidebar logo", () => {
|
|||
);
|
||||
|
||||
const scopeTrigger = screen.getByRole("button", { name: "common.explore" });
|
||||
expectCollapsedNavPill(scopeTrigger, "common.explore");
|
||||
expectDefaultNavPill(scopeTrigger, "common.explore");
|
||||
expect(scopeTrigger.querySelector(".lucide-chevron-down")).not.toBeInTheDocument();
|
||||
expectCollapsedNavPill(screen.getByRole("link", { name: "common.attachments" }), "common.attachments");
|
||||
|
||||
fireEvent.click(scopeTrigger);
|
||||
expectActiveNavPill(await screen.findByRole("button", { name: "common.explore", current: "page" }), "common.explore");
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ describe("sidebar route content", () => {
|
|||
["/Memos/ABC/", "memo"],
|
||||
["/memos/shares/token", "memo"],
|
||||
["/Memos/Shares/token/", "memo"],
|
||||
["/about", "empty"],
|
||||
["/404", "empty"],
|
||||
["/about", "common"],
|
||||
["/403", "common"],
|
||||
["/404", "common"],
|
||||
["/unknown", "common"],
|
||||
])("maps %s to %s content", (path, kind) => {
|
||||
expect(getSidebarRouteKind(path)).toBe(kind);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue