From 0ff87f251dafb32703d5f531e7b248ad0a581ee2 Mon Sep 17 00:00:00 2001 From: oliver <97427849+flamboh@users.noreply.github.com> Date: Sun, 20 Sep 2026 05:55:11 -0700 Subject: [PATCH] fix(web): keep citation comment when popover is dismissed (#10831) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> --- .../chat/AssistantCitationChip.test.tsx | 156 ++++++++++++++++++ .../components/chat/AssistantCitationChip.tsx | 61 +++++-- .../chat/AssistantCitationCommentEditor.tsx | 7 +- .../assistantCitationCommentDismissal.test.ts | 80 +++++++++ .../chat/assistantCitationCommentDismissal.ts | 21 +++ 5 files changed, 314 insertions(+), 11 deletions(-) create mode 100644 apps/web/src/components/chat/AssistantCitationChip.test.tsx create mode 100644 apps/web/src/components/chat/assistantCitationCommentDismissal.test.ts create mode 100644 apps/web/src/components/chat/assistantCitationCommentDismissal.ts diff --git a/apps/web/src/components/chat/AssistantCitationChip.test.tsx b/apps/web/src/components/chat/AssistantCitationChip.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7d664f2379834b7e72628621293185aaead34a3c --- /dev/null +++ b/apps/web/src/components/chat/AssistantCitationChip.test.tsx @@ -0,0 +1,156 @@ +import { + ASSISTANT_CITATION_MAX_COMMENT_LENGTH, + EnvironmentId, + MessageId, + ThreadId, +} from "@t3tools/contracts"; +import { act, useState, type ReactNode } from "react"; +import { create, type ReactTestRenderer } from "react-test-renderer"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; + +import type { AssistantCitationSourceAnchor } from "~/lib/assistantTextSelection"; + +const mocks = vi.hoisted(() => ({ observeSource: vi.fn(), dispose: vi.fn() })); +vi.mock("./AssistantCitationSource", () => ({ + observeAssistantCitationCommentSource: mocks.observeSource, +})); +vi.mock("@tanstack/react-router", () => ({ + useNavigate: () => vi.fn(), + Link: ({ children }: { children: ReactNode }) => {children}, +})); +// Keep the real chip/editor lifecycle while replacing DOM positioning and floating layers. +vi.mock("../ui/tooltip", () => ({ + Tooltip: ({ children }: { children: ReactNode }) => <>{children}, + TooltipTrigger: ({ render }: { render: ReactNode }) => render, + TooltipPopup: ({ children }: { children: ReactNode }) => <>{children}, +})); +vi.mock("../ui/popover", () => ({ + Popover: ({ children }: { children: ReactNode }) => <>{children}, + PopoverTrigger: ({ children }: { children: ReactNode }) => , + PopoverPopup: ({ children }: { children: ReactNode }) => <>{children}, +})); +vi.mock("../ui/button", () => ({ + Button: (props: React.ComponentProps<"button">) =>