diff --git a/app/src/components/model/panels/DescriptionPreview.js b/app/src/components/model/panels/DescriptionPreview.js
new file mode 100644
index 00000000..7e28db25
--- /dev/null
+++ b/app/src/components/model/panels/DescriptionPreview.js
@@ -0,0 +1,96 @@
+import { marked } from "marked";
+import DOMPurify from "dompurify";
+import Box from "@mui/material/Box";
+
+marked.use({
+ extensions: [
+ {
+ name: "heading",
+ renderer({ text, depth }) {
+ return `${text}`;
+ },
+ },
+ {
+ name: "image",
+ renderer(_) {
+ return "Images are not supported";
+ },
+ },
+ {
+ name: "link",
+ renderer(token) {
+ if (!token.href) {
+ return `
${token.text} (link without href)
`;
+ }
+ return `${token.text}`;
+ },
+ },
+ {
+ name: "code",
+ renderer(token) {
+ if (!token.text) {
+ return "";
+ }
+ if (token.lang) {
+ return `\`\`\`${token.lang}
${token.text}
\`\`\`
`;
+ }
+ return `\`\`\`
${token.text}
\`\`\`
`;
+ },
+ },
+ {
+ name: "list",
+ renderer(token) {
+ const itemList = token.items.map((i) => {
+ return (
+ "" + i.raw.replace(/\n+$/, "") + ""
+ );
+ });
+ if (token.ordered) {
+ return `${itemList.join("\n")}
`;
+ } else {
+ return ``;
+ }
+ },
+ },
+ ],
+});
+
+const domPurityConfig = {
+ USE_PROFILES: { html: true },
+ FORBID_TAGS: ["img"],
+ ADD_ATTR: ["target"],
+};
+
+export const DescriptionPreview = ({
+ description,
+ readOnly,
+ showDescriptionTextField,
+ sx = {},
+}) => {
+ if (!description) {
+ showDescriptionTextField(true);
+ return null;
+ }
+
+ const sanitizedHtml = DOMPurify.sanitize(
+ marked.parse(description),
+ domPurityConfig
+ );
+
+ if (readOnly) {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+};
diff --git a/app/src/components/model/panels/left/ComponentTab.js b/app/src/components/model/panels/left/ComponentTab.js
index ff046266..c9f655c6 100644
--- a/app/src/components/model/panels/left/ComponentTab.js
+++ b/app/src/components/model/panels/left/ComponentTab.js
@@ -14,8 +14,7 @@ import { MultipleSystemsDropdown } from "../../../elements/MultipleSystemsDropdo
import { COMPONENT_TYPE } from "../../board/constants";
import { useSelectedComponent } from "../../hooks/useSelectedComponent";
import { TechStacksDropdown } from "./TechStackDropdown";
-import { DescriptionPreview } from "./DescriptionPreview";
-import { set } from "lodash";
+import { DescriptionPreview } from "../DescriptionPreview";
export function ComponentTab() {
const dispatch = useDispatch();
@@ -214,11 +213,14 @@ export function ComponentTab() {
/>
)}
{(showDescriptionPreview || readOnly) && (
-
+ <>
+ Description
+
+ >
)}
diff --git a/app/src/components/model/panels/left/DescriptionPreview.js b/app/src/components/model/panels/left/DescriptionPreview.js
deleted file mode 100644
index 0a802948..00000000
--- a/app/src/components/model/panels/left/DescriptionPreview.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { marked } from "marked";
-import DOMPurify from "dompurify";
-import Box from "@mui/material/Box";
-import Typography from "@mui/material/Typography";
-
-marked.use({
- extensions: [
- {
- name: "heading",
- renderer({ text, depth }) {
- return `${text}`;
- },
- },
- {
- name: "image",
- renderer(_) {
- return "Images are not supported";
- },
- },
- {
- name: "list",
- renderer(token) {
- const itemList = token.items.map((i) => {
- return (
- "" + i.raw.replace(/\n+$/, "") + ""
- );
- });
- if (token.ordered) {
- return `${itemList.join("\n")}
`;
- } else {
- return ``;
- }
- },
- },
- ],
-});
-
-export const DescriptionPreview = ({
- description,
- showDescriptionTextField,
- readOnly,
-}) => {
- const sanitizedHtml = DOMPurify.sanitize(marked.parse(description));
-
- if (readOnly) {
- return (
- <>
- Description
-
- >
- );
- } else {
- return (
- <>
- Description
-
- >
- );
- }
-};
diff --git a/app/src/components/model/panels/right/EditableTypography.js b/app/src/components/model/panels/right/EditableTypography.js
index da492ce4..5ead7fd3 100644
--- a/app/src/components/model/panels/right/EditableTypography.js
+++ b/app/src/components/model/panels/right/EditableTypography.js
@@ -1,5 +1,48 @@
import { Input, Typography } from "@mui/material";
import { useEffect, useState } from "react";
+import Box from "@mui/material/Box";
+import { marked } from "marked";
+import DOMPurify from "dompurify";
+import { DescriptionPreview } from "../DescriptionPreview";
+
+marked.use({
+ extensions: [
+ {
+ name: "heading",
+ renderer({ text, depth }) {
+ return `${text}`;
+ },
+ },
+ {
+ name: "image",
+ renderer(_) {
+ return "Images are not supported";
+ },
+ },
+ {
+ name: "link",
+ renderer(token) {
+ console.log({ token });
+ return `${token.text}`;
+ },
+ },
+ {
+ name: "list",
+ renderer(token) {
+ const itemList = token.items.map((i) => {
+ return (
+ "" + i.raw.replace(/\n+$/, "") + ""
+ );
+ });
+ if (token.ordered) {
+ return `${itemList.join("\n")}
`;
+ } else {
+ return ``;
+ }
+ },
+ },
+ ],
+});
export function EditableTypography({
text,
@@ -63,14 +106,8 @@ export function EditableTypography({
}}
/>
) : (
- {
- if (!readOnly) {
- setIsEditing(!isEditing);
- }
- }}
+
- {value || placeholder}
-
+ readOnly={readOnly}
+ showDescriptionTextField={() => setIsEditing(!isEditing)}
+ />
)}
>
);
diff --git a/app/src/components/model/panels/right/Suggestion.js b/app/src/components/model/panels/right/Suggestion.js
index 4c953ff7..33919de2 100644
--- a/app/src/components/model/panels/right/Suggestion.js
+++ b/app/src/components/model/panels/right/Suggestion.js
@@ -15,6 +15,65 @@ import {
} from "../../../../api/gram/suggestions";
import { useModelID } from "../../hooks/useModelID";
import { useSelectedComponent } from "../../hooks/useSelectedComponent";
+import { marked } from "marked";
+import DOMPurify from "dompurify";
+import { DescriptionPreview } from "../DescriptionPreview";
+
+function SuggestionDescription({ description }) {
+ marked.use({
+ extensions: [
+ {
+ name: "heading",
+ renderer({ text, depth }) {
+ return `${text}`;
+ },
+ },
+ {
+ name: "image",
+ renderer(_) {
+ return "Images are not supported";
+ },
+ },
+ {
+ name: "list",
+ renderer(token) {
+ const itemList = token.items.map((i) => {
+ return (
+ "" +
+ i.raw.replace(/\n+$/, "") +
+ ""
+ );
+ });
+ if (token.ordered) {
+ return `${itemList.join("\n")}
`;
+ } else {
+ return ``;
+ }
+ },
+ },
+ ],
+ });
+
+ const domPurityConfig = {
+ USE_PROFILES: { html: true },
+ FORBID_TAGS: ["img", "table", "tr", "td", "th"],
+ };
+
+ const sanitizedHtml = DOMPurify.sanitize(
+ marked.parse(description),
+ domPurityConfig
+ );
+
+ return (
+
+
+ Description:
+
+
+ {description}
+
+ );
+}
function SuggestionMitigations({ suggestion, threatSuggestions }) {
const threatsMitigated = suggestion?.mitigates?.filter((m) =>
@@ -81,17 +140,15 @@ export function Suggestion({ suggestion, rejected, readOnly, isControl }) {
{suggestion.title}
{suggestion.description && (
-
- {suggestion.description}
-
+ />
)}