feat: parse resource metadata values as markdown (#10521)

This commit is contained in:
Kayla Washburn
2023-11-07 10:34:24 -07:00
committed by GitHub
parent 43a867441a
commit 9e4558ae3a
3 changed files with 122 additions and 8 deletions
+71 -5
View File
@@ -5,13 +5,14 @@ import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import { FC, memo } from "react";
import ReactMarkdown from "react-markdown";
import { type Interpolation, type Theme } from "@emotion/react";
import isEqual from "lodash/isEqual";
import { type FC, memo } from "react";
import ReactMarkdown, { type Options } from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import gfm from "remark-gfm";
import { colors } from "theme/colors";
import { darcula } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { type Interpolation, type Theme } from "@emotion/react";
interface MarkdownProps {
/**
@@ -20,10 +21,15 @@ interface MarkdownProps {
children: string;
className?: string;
/**
* Can override the behavior of the generated elements
*/
components?: Options["components"];
}
export const Markdown: FC<MarkdownProps> = (props) => {
const { children, className } = props;
const { children, className, components = {} } = props;
return (
<ReactMarkdown
@@ -106,6 +112,8 @@ export const Markdown: FC<MarkdownProps> = (props) => {
th: ({ children }) => {
return <TableCell>{children}</TableCell>;
},
...components,
}}
>
{children}
@@ -113,7 +121,65 @@ export const Markdown: FC<MarkdownProps> = (props) => {
);
};
export const MemoizedMarkdown = memo(Markdown);
interface MarkdownInlineProps {
/**
* The Markdown text to parse and render
*/
children: string;
className?: string;
/**
* Can override the behavior of the generated elements
*/
components?: Options["components"];
}
/**
* Supports a strict subset of Markdown that bahaves well as inline/confined content.
*/
export const InlineMarkdown: FC<MarkdownInlineProps> = (props) => {
const { children, className, components = {} } = props;
return (
<ReactMarkdown
className={className}
allowedElements={["p", "em", "strong", "a", "pre", "code"]}
unwrapDisallowed
components={{
p: ({ children }) => <>{children}</>,
a: ({ href, target, children }) => (
<Link href={href} target={target}>
{children}
</Link>
),
code: ({ node, className, children, style, ...props }) => (
<code
css={(theme) => ({
padding: "1px 4px",
background: theme.palette.divider,
borderRadius: 4,
color: theme.palette.text.primary,
fontSize: 14,
})}
{...props}
>
{children}
</code>
),
...components,
}}
>
{children}
</ReactMarkdown>
);
};
export const MemoizedMarkdown = memo(Markdown, isEqual);
export const MemoizedInlineMarkdown = memo(InlineMarkdown, isEqual);
const markdownStyles: Interpolation<Theme> = (theme: Theme) => ({
fontSize: 16,
+13 -3
View File
@@ -1,10 +1,12 @@
import { type FC, useState } from "react";
import { type FC, type PropsWithChildren, useState } from "react";
import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import { type CSSObject, type Interpolation, type Theme } from "@emotion/react";
import { Children } from "react";
import type { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated";
import { DropdownArrow } from "../DropdownArrow/DropdownArrow";
import { CopyableValue } from "../CopyableValue/CopyableValue";
import { MemoizedInlineMarkdown } from "../Markdown/Markdown";
import { Stack } from "../Stack/Stack";
import { ResourceAvatar } from "./ResourceAvatar";
import { SensitiveValue } from "./SensitiveValue";
@@ -72,6 +74,14 @@ export interface ResourceCardProps {
agentRow: (agent: WorkspaceAgent) => JSX.Element;
}
const p = ({ children }: PropsWithChildren) => {
const childrens = Children.toArray(children);
if (childrens.every((child) => typeof child === "string")) {
return <CopyableValue value={childrens.join("")}>{children}</CopyableValue>;
}
return <>{children}</>;
};
export const ResourceCard: FC<ResourceCardProps> = ({ resource, agentRow }) => {
const [shouldDisplayAllMetadata, setShouldDisplayAllMetadata] =
useState(false);
@@ -136,9 +146,9 @@ export const ResourceCard: FC<ResourceCardProps> = ({ resource, agentRow }) => {
{meta.sensitive ? (
<SensitiveValue value={meta.value} />
) : (
<CopyableValue value={meta.value}>
<MemoizedInlineMarkdown components={{ p }}>
{meta.value}
</CopyableValue>
</MemoizedInlineMarkdown>
)}
</div>
</div>
@@ -58,6 +58,44 @@ const reallyLong = {
sensitive: false,
};
export const Markdown: Story = {
args: {
resources: [
{
...nullDevice,
type: "workspace",
id: "1",
name: "Workspace",
metadata: [
{ key: "text", value: "hello", sensitive: false },
{ key: "link", value: "[hello](#)", sensitive: false },
{ key: "b/i", value: "_hello_, **friend**!", sensitive: false },
{ key: "coder", value: "`beep boop`", sensitive: false },
],
},
// bits of Markdown that are intentionally not supported here
{
...nullDevice,
type: "unsupported",
id: "2",
name: "Unsupported",
metadata: [
{
key: "multiple paragraphs",
value: `home,
home on the range`,
sensitive: false,
},
{ key: "heading", value: "# HI", sensitive: false },
{ key: "image", value: "![go](/icon/go.svg)", sensitive: false },
],
},
],
},
};
export const BunchOfDevicesWithMetadata: Story = {
args: {
resources: [