Skip to content

Commit

Permalink
feat: support markdown for description
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyouxik authored and Pauline Didier committed Aug 8, 2024
1 parent b2652f9 commit 46eab30
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 16 deletions.
70 changes: 56 additions & 14 deletions app/src/components/model/panels/left/ComponentTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import {
TextField,
Typography,
} from "@mui/material";
import { useEffect, useState } from "react";
import { useEffect, useState, useRef } from "react";
import { useDispatch } from "react-redux";
import { patchComponent } from "../../../../actions/model/patchComponent";
import { useReadOnly } from "../../../../hooks/useReadOnly";
import { MultipleSystemsDropdown } from "../../../elements/MultipleSystemsDropdown";
import { COMPONENT_TYPE } from "../../board/constants";
import { useSelectedComponent } from "../../hooks/useSelectedComponent";
import { TechStacksDropdown } from "./TechStackDropdown";
import { DescriptionPreview } from "./DescriptionPreview";
import { set } from "lodash";

export function ComponentTab() {
const dispatch = useDispatch();
Expand All @@ -24,6 +26,15 @@ export function ComponentTab() {
const { type, classes, systems } = component;
const [name, setName] = useState(component.name);
const [description, setDescription] = useState(component.description || "");
const [showDescriptionPreview, setShowDescriptionPreview] = useState(
component.description !== "" || readOnly
);
const descriptionTextFieldRef = useRef(null);

function showDescriptionTextField() {
setShowDescriptionPreview(false);
setTimeout(() => descriptionTextFieldRef.current.focus(), 1); // Time out needed for the ref to be set
}
// const [type, setType] = useState(component.type);
// const [techStacks, setTechStacks] = useState(component.classes || []);

Expand All @@ -38,8 +49,22 @@ export function ComponentTab() {
setDescription(
component.description === undefined ? "" : component.description
);

setShowDescriptionPreview((_) => {
if (readOnly) {
return true;
}
if (!component.description) {
return false;
}
if (component.description.trim() === "") {
return false;
}
return true;
});
}, [component.description]);

useEffect(() => {}, [component.description]);
// useEffect(() => {
// setType(component.type);
// }, [component.type]);
Expand All @@ -51,6 +76,14 @@ export function ComponentTab() {
// useEffect(() => {
// setSystemId(component.systemId === undefined ? "" : component.systemId);
// }, [component.systemId]);
function handleDescriptionOnBlur(newFields) {
if (description.trim() === "") {
setShowDescriptionPreview(false);
} else {
setShowDescriptionPreview(true);
}
updateFields(newFields);
}

function updateFields(newFields) {
dispatch(
Expand Down Expand Up @@ -93,7 +126,6 @@ export function ComponentTab() {
e.target.blur();
}
}

// console.log(systems, classes);

return (
Expand Down Expand Up @@ -166,18 +198,28 @@ export function ComponentTab() {
}}
readOnly={readOnly}
/>

<TextField
fullWidth
multiline
variant="standard"
label="Description"
disabled={readOnly}
value={description}
onBlur={() => updateFields({ description })}
onChange={(e) => setDescription(e.target.value)}
onKeyDown={(e) => shouldBlur(e)}
/>
{!showDescriptionPreview && !readOnly && (
<TextField
fullWidth
multiline
variant="standard"
label="Description"
placeholder="Explain the purpose and function of this component"
disabled={readOnly}
value={description}
inputRef={(e) => (descriptionTextFieldRef.current = e)}
onBlur={() => handleDescriptionOnBlur({ description })}
onChange={(e) => setDescription(e.target.value)}
onKeyDown={(e) => shouldBlur(e)}
/>
)}
{(showDescriptionPreview || readOnly) && (
<DescriptionPreview
description={description}
showDescriptionTextField={showDescriptionTextField}
readOnly={readOnly}
/>
)}
</Box>
</CardContent>
</Card>
Expand Down
63 changes: 63 additions & 0 deletions app/src/components/model/panels/left/DescriptionPreview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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 `<h${depth + 1}>${text}</h${depth + 1}>`;
},
},
{
name: "image",
renderer(_) {
return "Images are not supported";
},
},
{
name: "list",
renderer(token) {
const itemList = token.items.map((i) => {
return (
"<li style='margin-bottom:0'>" + i.raw.replace(/\n+$/, "") + "</li>"
);
});
if (token.ordered) {
return `<ol type="1">${itemList.join("\n")}</ol>`;
} else {
return `<ul>${itemList.join("\n")}</ul>`;
}
},
},
],
});

export const DescriptionPreview = ({
description,
showDescriptionTextField,
readOnly,
}) => {
const sanitizedHtml = DOMPurify.sanitize(marked.parse(description));

if (readOnly) {
return (
<>
<Typography variant="body1">Description</Typography>
<Box dangerouslySetInnerHTML={{ __html: sanitizedHtml }}></Box>
</>
);
} else {
return (
<>
<Typography variant="body1">Description</Typography>
<Box
onClick={showDescriptionTextField}
dangerouslySetInnerHTML={{ __html: sanitizedHtml }}
></Box>
</>
);
}
};
20 changes: 19 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
"typescript": "^5.2.2"
},
"dependencies": {
"log4js": "^6.9.1"
"dompurify": "^3.1.6",
"log4js": "^6.9.1",
"marked": "^14.0.0"
}
}

0 comments on commit 46eab30

Please sign in to comment.