Files
multica/packages/views/issues/components/issue-chip.test.tsx
Naiyuan Qing 4c7fefd143 fix(editor): cap issue mention chip at container width, no clickable gap (#4295)
#4288 swapped the chip cap from a fixed `max-w-72` to `max-w-[min(18rem,100%)]`.
A percentage max-width on a flex item is dropped while its flex-container wrapper
(`<a class="inline-flex">`) computes its own max-content size, so the wrapper
ballooned to the untruncated title width while the chip truncated to the cap —
leaving an empty, clickable strip after the visible chip.

Fix it as standard atomic-inline behavior instead of a fixed magic cap:
- IssueChip caps at `max-w-full` with the title truncating to an ellipsis, so it
  wraps to the next line as a unit and only truncates once a whole line can't
  hold it.
- Drop `inline-flex` from the editor NodeView `<a>` (issue + project) and the
  markdown AppLink so the chip's only wrapper is a plain inline box with a
  definite (line-based) percentage basis — no second flex container to balloon.

ProjectChip uses a definite `max-w-72`, so it never hit the gap; left as-is.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:11:07 +08:00

69 lines
2.1 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useQuery } from "@tanstack/react-query";
import { IssueChip } from "./issue-chip";
vi.mock("@tanstack/react-query", () => ({
useQuery: vi.fn(),
}));
vi.mock("@multica/core/hooks", () => ({
useWorkspaceId: () => "workspace-1",
}));
vi.mock("@multica/core/issues/queries", () => ({
issueListOptions: () => ({ queryKey: ["issues"] }),
issueDetailOptions: (_workspaceId: string, issueId: string) => ({
queryKey: ["issue", issueId],
}),
}));
vi.mock("./status-icon", () => ({
StatusIcon: ({ className }: { className?: string }) => (
<svg data-testid="status-icon" className={className} />
),
}));
const mockUseQuery = vi.mocked(useQuery);
describe("IssueChip", () => {
beforeEach(() => {
mockUseQuery.mockImplementation((options: { queryKey?: readonly unknown[] }) => {
if (options.queryKey?.[0] === "issues") {
return {
data: [
{
id: "issue-1",
identifier: "MUL-3405",
title: "A very long issue title that should stay inside a narrow chat bubble",
status: "todo",
},
],
} as ReturnType<typeof useQuery>;
}
return { data: undefined } as ReturnType<typeof useQuery>;
});
});
it("caps the chip to its parent container and truncates the title", () => {
render(<IssueChip issueId="issue-1" />);
const chip = screen.getByText("MUL-3405").closest(".issue-mention");
expect(chip).toHaveClass("min-w-0", "max-w-full");
expect(screen.getByText("A very long issue title that should stay inside a narrow chat bubble"))
.toHaveClass("min-w-0", "truncate");
});
it("truncates unresolved fallback labels inside the chip width", () => {
render(
<IssueChip
issueId="missing-issue"
fallbackLabel="MUL-999999999999999999999999999999999"
/>,
);
expect(screen.getByText("MUL-999999999999999999999999999999999"))
.toHaveClass("min-w-0", "truncate");
});
});