fix(ui): refine compact navigation and memo actions
This commit is contained in:
parent
5a65350400
commit
ce0dd23d2f
10 changed files with 194 additions and 31 deletions
|
|
@ -437,7 +437,7 @@ const GlobalNavigation = () => {
|
|||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<nav className={cn("flex h-9 items-center gap-1", SIDEBAR_RAIL_CLASSES)} aria-label="Primary">
|
||||
<nav className={cn("flex h-8 items-center gap-1", SIDEBAR_RAIL_CLASSES)} aria-label="Primary">
|
||||
{currentUser && (
|
||||
<DropdownMenu
|
||||
onOpenChange={(open, eventDetails) => {
|
||||
|
|
@ -505,7 +505,7 @@ const GlobalNavigation = () => {
|
|||
<span
|
||||
data-sidebar-trailing
|
||||
className={cn(
|
||||
"absolute -end-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[9px] font-semibold leading-4 text-primary-foreground transition-[opacity,scale] duration-200 ease-out motion-reduce:transition-none",
|
||||
"absolute -end-0.5 top-0 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[9px] font-semibold leading-4 text-primary-foreground transition-[opacity,scale] duration-200 ease-out motion-reduce:transition-none",
|
||||
item.count > 0 ? "scale-100 opacity-100" : "scale-50 opacity-0",
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CheckIcon, ChevronDownIcon, LoaderCircleIcon, type LucideIcon, PlusIcon } from "lucide-react";
|
||||
import { CheckIcon, ChevronsUpDownIcon, LoaderCircleIcon, type LucideIcon, PlusIcon } from "lucide-react";
|
||||
import { type ReactNode, useRef, useState } from "react";
|
||||
import CreateSpaceDialog from "@/components/CreateSpaceDialog";
|
||||
import MemosLogo from "@/components/MemosLogo";
|
||||
|
|
@ -92,7 +92,7 @@ function SpaceSwitcher({ className, size = "md" }: { className?: string; size?:
|
|||
aria-label={`${t("space.switch")}: ${currentContextLabel}`}
|
||||
title={currentContextLabel}
|
||||
className={cn(
|
||||
"group text-start transition-colors hover:bg-sidebar-accent/65 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/40",
|
||||
"text-start transition-colors hover:bg-sidebar-accent/65 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/40",
|
||||
size === "header" ? sidebarSurfaceVariants({ role: "headerBrand" }) : sidebarSurfaceVariants({ role: "mobileBrand" }),
|
||||
className,
|
||||
)}
|
||||
|
|
@ -127,10 +127,7 @@ function SpaceSwitcher({ className, size = "md" }: { className?: string; size?:
|
|||
<MemosLogo compact size={brandSize} />
|
||||
)}
|
||||
</span>
|
||||
<ChevronDownIcon
|
||||
className="size-3.5 shrink-0 text-muted-foreground/70 transition-transform duration-150 group-data-[popup-open]:rotate-180 motion-reduce:transition-none"
|
||||
strokeWidth={1.8}
|
||||
/>
|
||||
<ChevronsUpDownIcon aria-hidden="true" className="size-3.5 shrink-0 text-muted-foreground/70" strokeWidth={1.8} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { State } from "@/types/proto/api/v1/common_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { useMemoActionHandlers } from "./hooks";
|
||||
|
|
@ -63,8 +64,20 @@ const MemoActionMenu = (props: MemoActionMenuProps) => {
|
|||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger render={<Button variant="ghost" size="icon" className="size-4" />}>
|
||||
<MoreVerticalIcon className="text-muted-foreground" />
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t("common.more")}
|
||||
className={cn(
|
||||
"text-muted-foreground/70 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/50 data-popup-open:bg-accent data-popup-open:text-accent-foreground",
|
||||
props.className,
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MoreVerticalIcon className="size-4" strokeWidth={1.8} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" sideOffset={2}>
|
||||
{/* Edit actions (non-readonly, non-archived) */}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
|||
import { useInstance } from "@/contexts/InstanceContext";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { useReactionActions } from "./hooks";
|
||||
|
||||
interface Props {
|
||||
|
|
@ -14,6 +15,7 @@ interface Props {
|
|||
|
||||
const ReactionSelector = (props: Props) => {
|
||||
const { memo, className, onOpenChange } = props;
|
||||
const t = useTranslate();
|
||||
const [open, setOpen] = useState(false);
|
||||
const { memoRelatedSetting } = useInstance();
|
||||
|
||||
|
|
@ -30,17 +32,18 @@ const ReactionSelector = (props: Props) => {
|
|||
return (
|
||||
<Popover open={open} onOpenChange={handleOpenChange}>
|
||||
<PopoverTrigger
|
||||
nativeButton={false}
|
||||
render={
|
||||
<span
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("setting.memo.add-reaction")}
|
||||
className={cn(
|
||||
"h-7 w-7 flex justify-center items-center rounded-full border cursor-pointer transition-all hover:opacity-80",
|
||||
"flex size-7 cursor-pointer items-center justify-center rounded-full border text-muted-foreground transition-all hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SmilePlusIcon className="w-4 h-4 mx-auto text-muted-foreground" />
|
||||
<SmilePlusIcon className="mx-auto size-4" />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="center" className="max-w-[90vw] sm:max-w-md">
|
||||
<div className="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 gap-1 max-h-64 overflow-y-auto">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { BookmarkIcon } from "lucide-react";
|
|||
import { useCallback, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import RelativeTime from "@/components/RelativeTime";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useNewMemo } from "@/contexts/NewMemoContext";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
|
|
@ -21,6 +22,9 @@ import { createMemoNavigationState } from "../navigation";
|
|||
import type { MemoHeaderProps } from "../types";
|
||||
import MemoSpaceBadge from "./MemoSpaceBadge";
|
||||
|
||||
const MEMO_HEADER_ACTION_CLASSES =
|
||||
"size-6 shrink-0 rounded-md border-none bg-transparent text-muted-foreground transition-colors hover:bg-accent hover:text-foreground hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/50 data-popup-open:bg-accent data-popup-open:text-foreground";
|
||||
|
||||
const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, showPinned, showSpace }) => {
|
||||
const t = useTranslate();
|
||||
const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false);
|
||||
|
|
@ -82,10 +86,14 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, sh
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-end items-center select-none shrink-0 gap-2">
|
||||
<div data-slot="memo-header-actions" className="flex shrink-0 select-none flex-row items-center justify-end gap-1">
|
||||
{currentUser && !isArchived && (
|
||||
<ReactionSelector
|
||||
className={cn("border-none w-auto h-auto", reactionSelectorOpen && "block!", "block sm:hidden sm:group-hover:block")}
|
||||
className={cn(
|
||||
MEMO_HEADER_ACTION_CLASSES,
|
||||
reactionSelectorOpen && "sm:flex!",
|
||||
"flex sm:hidden sm:group-hover:flex sm:group-focus-within:flex",
|
||||
)}
|
||||
memo={memo}
|
||||
onOpenChange={setReactionSelectorOpen}
|
||||
/>
|
||||
|
|
@ -93,10 +101,8 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, sh
|
|||
|
||||
{showVisibility && memo.visibility !== Visibility.PRIVATE && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<span className="flex justify-center items-center rounded-md hover:opacity-80">
|
||||
<VisibilityIcon visibility={memo.visibility} />
|
||||
</span>
|
||||
<TooltipTrigger aria-label={visibilityOption && t(visibilityOption.labelKey)} className={MEMO_HEADER_ACTION_CLASSES}>
|
||||
<VisibilityIcon visibility={memo.visibility} className="text-current" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{visibilityOption && t(visibilityOption.labelKey)}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
|
@ -105,8 +111,18 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, sh
|
|||
{showPinned && memo.pinned && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger render={<span className="cursor-pointer" />}>
|
||||
<BookmarkIcon className="w-4 h-auto text-primary" onClick={unpinMemo} />
|
||||
<TooltipTrigger
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t("common.unpin")}
|
||||
className={cn(MEMO_HEADER_ACTION_CLASSES, "text-primary hover:text-primary data-popup-open:text-primary")}
|
||||
onClick={unpinMemo}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<BookmarkIcon className="size-4" strokeWidth={1.8} />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{t("common.unpin")}</p>
|
||||
|
|
@ -115,7 +131,13 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, sh
|
|||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<MemoActionMenu memo={memo} parentScope={parentScope} readonly={readonly} onEdit={openEditor} />
|
||||
<MemoActionMenu
|
||||
memo={memo}
|
||||
parentScope={parentScope}
|
||||
readonly={readonly}
|
||||
className={MEMO_HEADER_ACTION_CLASSES}
|
||||
onEdit={openEditor}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -222,7 +222,8 @@ describe("App sidebar logo", () => {
|
|||
expect(switcher).not.toHaveClass("px-1");
|
||||
expect(switcher.firstElementChild).not.toHaveClass("flex-1");
|
||||
expect(within(switcher).getByText("Memos logo")).toHaveAttribute("data-logo-size", "header");
|
||||
expect(switcher.querySelector(".lucide-chevron-down")).not.toBeNull();
|
||||
expect(switcher.querySelector(".lucide-chevrons-up-down")).not.toBeNull();
|
||||
expect(switcher.querySelector(".lucide-chevron-down")).toBeNull();
|
||||
expect(compose).toHaveClass("size-7", "rounded-md", "border", "bg-background", "shadow-xs");
|
||||
expect(compose).not.toHaveClass("rounded-full");
|
||||
|
||||
|
|
@ -398,7 +399,7 @@ describe("App sidebar logo", () => {
|
|||
expect(screen.getByRole("link", { name: "Memos logo" })).toHaveAttribute("href", "/explore");
|
||||
expect(screen.queryByRole("button", { name: /^space\.switch:/ })).not.toBeInTheDocument();
|
||||
const primaryNavigation = screen.getByRole("navigation", { name: "Primary" });
|
||||
expect(primaryNavigation).toHaveClass("h-9", "items-center", "gap-1", "px-3");
|
||||
expect(primaryNavigation).toHaveClass("h-8", "items-center", "gap-1", "px-3");
|
||||
expect(primaryNavigation).not.toHaveClass("flex-col");
|
||||
const navigation = within(primaryNavigation);
|
||||
expectActiveNavPill(navigation.getByRole("link", { name: "common.explore" }), "common.explore");
|
||||
|
|
@ -523,6 +524,7 @@ describe("App sidebar logo", () => {
|
|||
const scopeTrigger = screen.getByRole("button", { name: "common.home" });
|
||||
expectActiveNavPill(scopeTrigger, "common.home");
|
||||
expect(scopeTrigger.querySelector(".lucide-chevron-down")).toBeInTheDocument();
|
||||
expect(scopeTrigger.querySelector(".lucide-chevrons-up-down")).not.toBeInTheDocument();
|
||||
expectCollapsedNavPill(screen.getByRole("link", { name: "common.attachments" }), "common.attachments");
|
||||
fireEvent.click(scopeTrigger);
|
||||
expect(await screen.findByRole("menuitem", { name: "common.home" })).toBeInTheDocument();
|
||||
|
|
|
|||
49
web/tests/memo-action-menu.test.tsx
Normal file
49
web/tests/memo-action-menu.test.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { create } from "@bufbuild/protobuf";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import MemoActionMenu from "@/components/MemoActionMenu";
|
||||
import { State } from "@/types/proto/api/v1/common_pb";
|
||||
import { MemoSchema } from "@/types/proto/api/v1/memo_service_pb";
|
||||
|
||||
const handlers = vi.hoisted(() => ({
|
||||
handleTogglePinMemoBtnClick: vi.fn(),
|
||||
handleEditMemoClick: vi.fn(),
|
||||
handleToggleMemoStatusClick: vi.fn(),
|
||||
handleCopyLink: vi.fn(),
|
||||
handleCopyContent: vi.fn(),
|
||||
handleCheckAllTaskListItemsClick: vi.fn(),
|
||||
handleUncheckAllTaskListItemsClick: vi.fn(),
|
||||
handleDeleteMemoClick: vi.fn(),
|
||||
confirmDeleteMemo: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ConfirmDialog", () => ({
|
||||
default: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/MemoActionMenu/hooks", () => ({
|
||||
useMemoActionHandlers: () => handlers,
|
||||
}));
|
||||
|
||||
vi.mock("@/utils/i18n", () => ({
|
||||
useTranslate: () => (key: string) => key,
|
||||
}));
|
||||
|
||||
describe("MemoActionMenu", () => {
|
||||
it("uses the standard compact action surface and preserves its open state", async () => {
|
||||
const memo = create(MemoSchema, { name: "memos/1", state: State.NORMAL, pinned: false });
|
||||
render(<MemoActionMenu memo={memo} parentScope="preserve" />);
|
||||
|
||||
const trigger = screen.getByRole("button", { name: "common.more" });
|
||||
expect(trigger).toHaveClass("size-6", "rounded-md", "hover:bg-accent", "focus-visible:ring-2", "data-popup-open:bg-accent");
|
||||
expect(trigger).toHaveAttribute("aria-haspopup", "menu");
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "false");
|
||||
expect(trigger.querySelector(".lucide-ellipsis-vertical")).toHaveClass("size-4");
|
||||
|
||||
fireEvent.click(trigger);
|
||||
|
||||
await waitFor(() => expect(trigger).toHaveAttribute("aria-expanded", "true"));
|
||||
expect(trigger).toHaveAttribute("data-popup-open");
|
||||
expect(await screen.findByRole("menuitem", { name: "common.pin" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -5,6 +5,7 @@ import MemoHeader from "@/components/MemoView/components/MemoHeader";
|
|||
|
||||
const state = vi.hoisted(() => ({
|
||||
creator: undefined as { username: string; displayName: string; avatarUrl: string } | undefined,
|
||||
currentUser: undefined as { name: string } | undefined,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/RelativeTime", () => ({
|
||||
|
|
@ -12,11 +13,19 @@ vi.mock("@/components/RelativeTime", () => ({
|
|||
}));
|
||||
|
||||
vi.mock("@/components/MemoActionMenu", () => ({
|
||||
default: () => null,
|
||||
default: ({ className }: { className?: string }) => (
|
||||
<button type="button" aria-label="memo-actions" className={className}>
|
||||
Memo actions
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/MemoReactionListView", () => ({
|
||||
ReactionSelector: () => null,
|
||||
ReactionSelector: ({ className }: { className?: string }) => (
|
||||
<button type="button" aria-label="add-reaction" className={className}>
|
||||
Add reaction
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/UserAvatar", () => ({
|
||||
|
|
@ -50,7 +59,7 @@ vi.mock("@/components/MemoView/MemoViewContext", () => ({
|
|||
useMemoViewContext: () => ({
|
||||
memo: { name: "memos/123", visibility: 1, pinned: false, space: "spaces/product" },
|
||||
creator: state.creator,
|
||||
currentUser: undefined,
|
||||
currentUser: state.currentUser,
|
||||
parentPage: "/explore?filter=tagSearch%3Awork",
|
||||
parentScope: "preserve",
|
||||
isArchived: false,
|
||||
|
|
@ -82,6 +91,34 @@ const LocationProbe = () => {
|
|||
describe("MemoHeader navigation", () => {
|
||||
beforeEach(() => {
|
||||
state.creator = undefined;
|
||||
state.currentUser = undefined;
|
||||
});
|
||||
|
||||
it("uses one compact interaction surface for memo header actions", () => {
|
||||
state.currentUser = { name: "users/alice" };
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MemoHeader />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
const reaction = screen.getByRole("button", { name: "add-reaction" });
|
||||
const actions = screen.getByRole("button", { name: "memo-actions" });
|
||||
const actionRail = actions.closest('[data-slot="memo-header-actions"]');
|
||||
|
||||
expect(actionRail).toHaveClass("items-center", "gap-1");
|
||||
expect(reaction).toHaveClass("sm:group-focus-within:flex");
|
||||
for (const action of [reaction, actions]) {
|
||||
expect(action).toHaveClass(
|
||||
"size-6",
|
||||
"rounded-md",
|
||||
"hover:bg-accent",
|
||||
"hover:text-foreground",
|
||||
"focus-visible:ring-2",
|
||||
"data-popup-open:bg-accent",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([false, true])("uses a keyboard-operable timestamp and preserves origin when showCreator=%s", (showCreator) => {
|
||||
|
|
|
|||
36
web/tests/reaction-selector.test.tsx
Normal file
36
web/tests/reaction-selector.test.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { create } from "@bufbuild/protobuf";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import ReactionSelector from "@/components/MemoReactionListView/ReactionSelector";
|
||||
import { MemoSchema } from "@/types/proto/api/v1/memo_service_pb";
|
||||
|
||||
vi.mock("@/components/MemoReactionListView/hooks", () => ({
|
||||
useReactionActions: () => ({
|
||||
hasReacted: () => false,
|
||||
handleReactionClick: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/contexts/InstanceContext", () => ({
|
||||
useInstance: () => ({ memoRelatedSetting: { reactions: ["👍"] } }),
|
||||
}));
|
||||
|
||||
vi.mock("@/utils/i18n", () => ({
|
||||
useTranslate: () => (key: string) => key,
|
||||
}));
|
||||
|
||||
describe("ReactionSelector", () => {
|
||||
it("uses a named native trigger with a visible focus state", async () => {
|
||||
const memo = create(MemoSchema, { name: "memos/1", reactions: [] });
|
||||
render(<ReactionSelector memo={memo} />);
|
||||
|
||||
const trigger = screen.getByRole("button", { name: "setting.memo.add-reaction" });
|
||||
expect(trigger).toHaveClass("size-7", "rounded-full", "focus-visible:ring-2");
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "false");
|
||||
|
||||
fireEvent.click(trigger);
|
||||
|
||||
await waitFor(() => expect(trigger).toHaveAttribute("aria-expanded", "true"));
|
||||
expect(screen.getByRole("button", { name: "👍" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -64,7 +64,11 @@ describe("SpaceSwitcher", () => {
|
|||
it("lists Memos, every available Space, and the create entry", async () => {
|
||||
render(<SpaceSwitcher />);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "space.switch: common.memos" }));
|
||||
const trigger = screen.getByRole("button", { name: "space.switch: common.memos" });
|
||||
expect(trigger).toHaveAttribute("aria-haspopup", "menu");
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "false");
|
||||
fireEvent.click(trigger);
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
expect(await screen.findByRole("menuitemradio", { name: "Memos" })).toHaveAttribute("aria-checked", "true");
|
||||
const productRow = screen.getByRole("menuitemradio", { name: "Product" });
|
||||
|
|
@ -157,8 +161,8 @@ describe("SpaceSwitcher", () => {
|
|||
expect(trigger).not.toHaveClass("px-1");
|
||||
expect(title).toHaveClass("text-[14px]", "font-semibold", "leading-5");
|
||||
expect(mark).toHaveClass("size-5", "rounded-[5px]");
|
||||
expect(trigger.querySelector(".lucide-chevron-down")).not.toBeNull();
|
||||
expect(trigger.querySelector(".lucide-chevrons-up-down")).toBeNull();
|
||||
expect(trigger.querySelector(".lucide-chevrons-up-down")).not.toBeNull();
|
||||
expect(trigger.querySelector(".lucide-chevron-down")).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps duplicate identity in the header label but out of its geometry", () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue