content
+content
+content
+content
+inline snippet);
+
+ expect(screen.getByText("inline snippet")).toBeInTheDocument();
+ expect(screen.queryByTestId("copy-to-clipboard")).not.toBeInTheDocument();
+ });
+
+ it("should render a multiline code block with a copy button", () => {
+ render({"line1\nline2"});
+
+ expect(screen.getByText("line1 line2")).toBeInTheDocument();
+ expect(screen.getByTestId("copy-to-clipboard")).toBeInTheDocument();
+ });
+
+ it("should render a syntax-highlighted block with a copy button", () => {
+ render({"console.log('hi')"});
+
+ expect(screen.getByTestId("copy-to-clipboard")).toBeInTheDocument();
+ });
+
+ it("should copy code block content to clipboard", async () => {
+ const user = userEvent.setup();
+ render({"line1\nline2"});
+
+ await user.click(screen.getByTestId("copy-to-clipboard"));
+
+ await waitFor(() =>
+ expect(navigator.clipboard.readText()).resolves.toBe("line1\nline2"),
+ );
+ });
+});
diff --git a/frontend/src/components/features/markdown/code.tsx b/frontend/src/components/features/markdown/code.tsx
index 2a801f6848..ee04ce53b5 100644
--- a/frontend/src/components/features/markdown/code.tsx
+++ b/frontend/src/components/features/markdown/code.tsx
@@ -2,6 +2,7 @@ import React from "react";
import { ExtraProps } from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
+import { CopyableContentWrapper } from "#/components/shared/buttons/copyable-content-wrapper";
// See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#use-custom-components-syntax-highlight
@@ -15,6 +16,7 @@ export function code({
React.HTMLAttributes
- {String(children).replace(/\n$/, "")}
-
+
+ {codeString}
+
+