diff --git a/ui/src/components/DocumentAnnotationLayer.test.tsx b/ui/src/components/DocumentAnnotationLayer.test.tsx new file mode 100644 index 00000000000..4e49a5424ef --- /dev/null +++ b/ui/src/components/DocumentAnnotationLayer.test.tsx @@ -0,0 +1,99 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { DocumentAnnotationLayer } from "./DocumentAnnotationLayer"; + +const mockRangesForNormalizedSpan = vi.hoisted(() => vi.fn()); + +vi.mock("@/lib/document-annotation-selection", () => ({ + buildAnchorFromContainerSelection: vi.fn(), + getContainerTextOffset: vi.fn(), + rangesForNormalizedSpan: mockRangesForNormalizedSpan, +})); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +function makeRect(left: number, top: number, width: number, height: number): DOMRect { + return { + x: left, + y: top, + left, + top, + right: left + width, + bottom: top + height, + width, + height, + toJSON: () => ({}), + } as DOMRect; +} + +describe("DocumentAnnotationLayer", () => { + let container: HTMLDivElement; + let rectSpy: ReturnType; + let root: Root | null = null; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + mockRangesForNormalizedSpan.mockReturnValue([ + { + getClientRects: () => [makeRect(8, 12, 80, 18)], + }, + ]); + rectSpy = vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockReturnValue(makeRect(0, 0, 400, 300)); + }); + + afterEach(() => { + if (root) { + act(() => root?.unmount()); + root = null; + } + rectSpy.mockRestore(); + container.remove(); + vi.clearAllMocks(); + }); + + it("uses solid yellow backgrounds for annotation highlights in light and dark themes", async () => { + const body = document.createElement("div"); + body.textContent = "Annotated body text."; + root = createRoot(container); + + await act(async () => { + root?.render( + , + ); + await new Promise((resolve) => window.requestAnimationFrame(resolve)); + }); + + const highlights = Array.from(container.querySelectorAll(".paperclip-doc-annotation-highlight")); + expect(highlights).toHaveLength(4); + + for (const highlight of highlights) { + const backgroundClasses = Array.from(highlight.classList).filter((className) => + /^(dark:|hover:|dark:hover:)?bg-yellow-\d+$/.test(className) + || /^(dark:|hover:|dark:hover:)?bg-yellow-\d+\//.test(className), + ); + expect(backgroundClasses.some((className) => className.includes("/"))).toBe(false); + expect(backgroundClasses.some((className) => className.startsWith("bg-yellow-"))).toBe(true); + expect(backgroundClasses.some((className) => className.startsWith("dark:bg-yellow-"))).toBe(true); + } + }); +}); diff --git a/ui/src/components/DocumentAnnotationLayer.tsx b/ui/src/components/DocumentAnnotationLayer.tsx index 1c4e423b44b..98b67f70bef 100644 --- a/ui/src/components/DocumentAnnotationLayer.tsx +++ b/ui/src/components/DocumentAnnotationLayer.tsx @@ -268,12 +268,12 @@ export function DocumentAnnotationLayer({ "paperclip-doc-annotation-highlight pointer-events-auto absolute cursor-pointer rounded-none transition-colors", // base box treatment (replaces the previous baseline border) isResolved - ? "bg-yellow-300/25 outline outline-1 outline-dashed outline-offset-0 outline-yellow-700/45 hover:bg-yellow-300/35 dark:bg-yellow-300/18 dark:outline-yellow-200/45" + ? "bg-yellow-100 outline outline-1 outline-dashed outline-offset-0 outline-yellow-700/45 hover:bg-yellow-200 dark:bg-yellow-700 dark:outline-yellow-200/45 dark:hover:bg-yellow-600" : isStale - ? "bg-yellow-300/35 outline outline-2 outline-dashed outline-offset-0 outline-yellow-700/65 hover:bg-yellow-300/45 dark:bg-yellow-300/24 dark:outline-yellow-200/70" + ? "bg-yellow-200 outline outline-2 outline-dashed outline-offset-0 outline-yellow-700/65 hover:bg-yellow-300 dark:bg-yellow-600 dark:outline-yellow-200/70 dark:hover:bg-yellow-500" : isFocused - ? "bg-yellow-300/55 outline outline-2 outline-offset-0 outline-yellow-700/85 shadow-[0_0_0_1px_var(--color-background)] dark:bg-yellow-300/35 dark:outline-yellow-200/85" - : "bg-yellow-300/40 hover:bg-yellow-300/55 dark:bg-yellow-300/24 dark:hover:bg-yellow-300/34", + ? "bg-yellow-300 outline outline-2 outline-offset-0 outline-yellow-700/85 shadow-[0_0_0_1px_var(--color-background)] hover:bg-yellow-300 dark:bg-yellow-500 dark:outline-yellow-200/85 dark:hover:bg-yellow-400" + : "bg-yellow-200 hover:bg-yellow-300 dark:bg-yellow-600 dark:hover:bg-yellow-500", )} style={{ top: rect.top,