FN-8613: theme mailbox markdown links
Theme mailbox message anchors with dashboard tokens and verify link coverage. - Style markdown anchor default, visited, hover, and focus-visible states with design tokens - Cover inline, autolink, list, and table links while preserving file-path controls - Add a patch changeset for themed mailbox links Files changed: .changeset/theme-mailbox-markdown-links.md | 7 ++++ packages/dashboard/app/components/MailboxModal.css | 29 +++++++++++++ .../__tests__/MailboxMessageContent.test.tsx | 49 ++++++++++++++++++---- 3 files changed, 77 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-8613 Fusion-Task-Lineage: a8c7e8bb-8238-4018-bf95-38193f908eb4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/theme-mailbox-markdown-links.md
Normal file
7
.changeset/theme-mailbox-markdown-links.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Mailbox message links now use theme colors instead of default browser blue.
|
||||
category: fix
|
||||
dev: Markdown anchor states use dashboard tokens in shared MailboxMessageContent styling.
|
||||
@@ -411,6 +411,35 @@ Artifact-registration mail messages now render metadata-driven media and produci
|
||||
padding-left: var(--space-lg);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:Mailbox 2026-07-26-14:39:
|
||||
Markdown links in mailbox message bodies must use theme tokens: browser default blue
|
||||
and visited purple clash with dark themes and violate the dashboard token system.
|
||||
*/
|
||||
.mailbox-markdown a {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
|
||||
text-underline-offset: var(--space-2xs);
|
||||
border-radius: var(--radius-sm);
|
||||
overflow-wrap: anywhere;
|
||||
transition: color var(--transition-fast), text-decoration-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.mailbox-markdown a:visited {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.mailbox-markdown a:hover {
|
||||
color: color-mix(in srgb, var(--accent) 85%, var(--text));
|
||||
text-decoration-color: var(--accent);
|
||||
}
|
||||
|
||||
.mailbox-markdown a:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring-strong);
|
||||
}
|
||||
|
||||
.mailbox-markdown :not(pre) > code {
|
||||
padding: 0 var(--space-xs);
|
||||
border-radius: var(--radius-sm);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { describe, it, expect, afterEach, vi } from "vitest";
|
||||
import { render, cleanup, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { FileBrowserProvider } from "../../context/FileBrowserContext";
|
||||
import { MailboxMessageContent } from "../MailboxMessageContent";
|
||||
|
||||
const mailboxModalCss = readFileSync(resolve(__dirname, "../MailboxModal.css"), "utf8");
|
||||
|
||||
// FNXC:Markdown 2026-06-23-03:15: Mock the heavy `mermaid` library so the mermaid
|
||||
// rendering tests do not pull in the real parser/renderer bundle. The component
|
||||
// lazy-imports `mermaid` (default export), so we mock the module default.
|
||||
@@ -61,15 +65,44 @@ describe("MailboxMessageContent", () => {
|
||||
expect(pre?.textContent).toContain("npm install");
|
||||
});
|
||||
|
||||
it("renders links with target=_blank and noopener noreferrer", () => {
|
||||
render(
|
||||
<MailboxMessageContent content="See [docs](https://example.com/docs)." />,
|
||||
it("themes markdown links consistently across inline, autolink, list, and table content", () => {
|
||||
const content = [
|
||||
"See [inline docs](https://example.com/inline).",
|
||||
"Visit https://example.com/autolink.",
|
||||
"- [list docs](https://example.com/list)",
|
||||
"",
|
||||
"| docs |",
|
||||
"| --- |",
|
||||
"| [table docs](https://example.com/table) |",
|
||||
"",
|
||||
"See packages/dashboard/app/App.tsx for context.",
|
||||
].join("\n");
|
||||
const { container } = render(
|
||||
<FileBrowserProvider openFile={vi.fn()}>
|
||||
<MailboxMessageContent content={content} />
|
||||
</FileBrowserProvider>,
|
||||
);
|
||||
const link = screen.getByRole("link", { name: "docs" });
|
||||
expect(link.getAttribute("href")).toBe("https://example.com/docs");
|
||||
expect(link.getAttribute("target")).toBe("_blank");
|
||||
expect(link.getAttribute("rel")).toContain("noopener");
|
||||
expect(link.getAttribute("rel")).toContain("noreferrer");
|
||||
|
||||
const links = screen.getAllByRole("link");
|
||||
expect(links).toHaveLength(4);
|
||||
for (const link of links) {
|
||||
expect(link.closest(".mailbox-markdown")).not.toBeNull();
|
||||
expect(link).toHaveAttribute("target", "_blank");
|
||||
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
||||
}
|
||||
expect(screen.getByRole("button", { name: "packages/dashboard/app/App.tsx" }))
|
||||
.toHaveClass("file-path-link");
|
||||
expect(container.querySelector("a.file-path-link")).toBeNull();
|
||||
});
|
||||
|
||||
it("defines token-only mailbox markdown anchor styles", () => {
|
||||
const anchorRule = mailboxModalCss.match(/\.mailbox-markdown a\s*\{([^}]*)\}/)?.[1];
|
||||
expect(mailboxModalCss).toMatch(/\.mailbox-markdown a\b/);
|
||||
expect(anchorRule).toBeDefined();
|
||||
expect(anchorRule).not.toMatch(/#[0-9a-f]{3,8}\b|rgba\(/i);
|
||||
expect(mailboxModalCss).toMatch(/\.mailbox-markdown a:visited\s*\{/);
|
||||
expect(mailboxModalCss).toMatch(/\.mailbox-markdown a:hover\s*\{/);
|
||||
expect(mailboxModalCss).toMatch(/\.mailbox-markdown a:focus-visible\s*\{/);
|
||||
});
|
||||
|
||||
it("renders GFM tables with the mailbox-scoped class", () => {
|
||||
|
||||
Reference in New Issue
Block a user