Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion frontend/src/v2/__tests__/V2MessageRow.realMarkdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// asserted here is the one a reader would click.
import React from 'react';
import { render, screen } from '@testing-library/react';
import { linkifyBareUrls, prUrlsToRefLinks } from '../components/V2MessageRow';
import { linkifyBareUrls, messageMarkdownComponents, prUrlsToRefLinks } from '../components/V2MessageRow';
import V2GithubPrCard from '../components/V2GithubPrCard';

const RealReactMarkdown = jest.requireActual('../../../node_modules/react-markdown/index.js').default;
Expand Down Expand Up @@ -72,6 +72,19 @@ describe('bare URL linkification through the real react-markdown', () => {
expect(text).toBe('see the PR now');
});

// TASK-306. react-markdown hands every component its mdast `node`; the
// renderers must not spread it onto the DOM as node="[object Object]".
it('rendered links and fenced code carry no node attribute', () => {
const { container, unmount } = render(
<RealReactMarkdown components={messageMarkdownComponents}>
{'see [docs](https://example.com/docs)\n\n```\nconst x = 1;\n```'}
</RealReactMarkdown>,
);
expect(container.querySelector('a')).not.toHaveAttribute('node');
expect(container.querySelector('pre')).not.toHaveAttribute('node');
unmount();
});

it('a PR URL in a sentence reads as a short ref, with its punctuation intact', () => {
const { container, unmount } = render(
<RealReactMarkdown>{prUrlsToRefLinks('Review: https://github.com/Team-Commonly/commonly/pull/12. Then merge.')}</RealReactMarkdown>,
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/v2/components/V2MessageRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { lazy, Suspense, useEffect, useRef, useState } from 'react';
import V2Lightbox, { type V2LightboxImage } from './V2Lightbox';
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import ReactMarkdown from 'react-markdown';
import ReactMarkdown, { type ExtraProps } from 'react-markdown';
import V2Avatar from './V2Avatar';
import V2GithubPrCard, { parseGithubPrUrls } from './V2GithubPrCard';
import V2ApprovalCard from './V2ApprovalCard';
Expand Down Expand Up @@ -75,9 +75,11 @@ export const MessageMarkdownImage = ({ src, alt }: React.ImgHTMLAttributes<HTMLI
return <span>{alt || src}</span>;
};

const messageMarkdownComponents = {
export const messageMarkdownComponents = {
img: MessageMarkdownImage,
a: ({ children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
// react-markdown passes its mdast `node` to every component; spreading it
// onto the DOM element renders node="[object Object]".
a: ({ children, node: _node, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & ExtraProps) => (
<a {...props} target="_blank" rel="noopener noreferrer">{renderWithMentions(children)}</a>
),
p: ({ children }: React.HTMLAttributes<HTMLParagraphElement>) => (
Expand All @@ -90,7 +92,7 @@ const messageMarkdownComponents = {
// `<pre>`. Both fall through to v2.css selectors `.v2-msg__content code`
// and `.v2-msg__content pre`. Mentions inside code are NOT transformed.
// Fenced code past six lines collapses (direction C).
pre: (props: React.HTMLAttributes<HTMLPreElement>) => <CollapsiblePre {...props} />,
pre: ({ node: _node, ...props }: React.HTMLAttributes<HTMLPreElement> & ExtraProps) => <CollapsiblePre {...props} />,
};

interface V2MessageRowProps {
Expand Down
Loading