Skip to content

Commit

Permalink
move jsonToMermaid from CaseContainer into utils, and add new functio…
Browse files Browse the repository at this point in the history
…n to sanitize strings for Mermaid
  • Loading branch information
nbarlowATI committed Apr 5, 2022
1 parent b533026 commit 100ad18
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 78 deletions.
112 changes: 112 additions & 0 deletions frontend/package-lock.json

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

1 change: 1 addition & 0 deletions frontend/package.json
Expand Up @@ -11,6 +11,7 @@
"file-saver": "^2.0.5",
"grommet": "^2.20.1",
"grommet-icons": "^4.7.0",
"jest-fetch-mock": "^3.0.3",
"jest-transform-stub": "^2.0.0",
"mermaid": "^8.13.10",
"pattern.css": "^1.0.0",
Expand Down
76 changes: 4 additions & 72 deletions frontend/src/components/CaseContainer.js
Expand Up @@ -3,16 +3,15 @@ import React, { Component } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Grid, Box, DropButton, Layer, Button, Text } from "grommet";
import { FormClose, ZoomIn, ZoomOut } from "grommet-icons";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";

import MermaidChart from "./Mermaid";
import configData from "../config.json";

import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import EditableText from "./EditableText.js";
import ItemViewer from "./ItemViewer.js";
import ItemEditor from "./ItemEditor.js";
import ItemCreator from "./ItemCreator.js";

import { jsonToMermaid } from "./utils.js";
import configData from "../config.json";
import "./CaseContainer.css";

class CaseContainer extends Component {
Expand Down Expand Up @@ -48,7 +47,7 @@ class CaseContainer extends Component {
assurance_case: json_response,
});
this.setState({
mermaid_md: this.jsonToMermaid(this.state.assurance_case),
mermaid_md: jsonToMermaid(this.state.assurance_case),
});
this.setState({ loading: false });
}
Expand Down Expand Up @@ -115,71 +114,6 @@ class CaseContainer extends Component {
}
}

jsonToMermaid(in_json) {
// function to convert the JSON response from a GET request to the /cases/id
// API endpoint, into the markdown string required for Mermaid to render a flowchart.

// Nodes in the flowchart will be named [TypeName]_[ID]
function getNodeName(itemType, itemId) {
return itemType + "_" + itemId;
}

function makeBox(text, shape) {
if (shape === "square") return "[" + text + "]";
else if (shape === "diamond") return "{" + text + "}";
else if (shape === "rounded") return "(" + text + ")";
else if (shape === "circle") return "((" + text + "))";
else if (shape === "data") return "[(" + text + ")]";
else return "";
}

let arrow = " --- ";
/// Recursive function to go down the tree adding components
function addTree(itemType, parent, parentNode, outputmd) {
// look up the 'API name', e.g. "goals" for "TopLevelNormativeGoal"
let thisType = configData.navigation[itemType]["db_name"];
let boxShape = configData.navigation[itemType]["shape"];
// loop over all objects of this type
for (let i = 0; i < parent[thisType].length; i++) {
let thisObj = parent[thisType][i];
let thisNode = getNodeName(itemType, thisObj.id);
if (parentNode != null) {
outputmd +=
parentNode +
arrow +
thisNode +
makeBox(thisObj.name, boxShape) +
"\n";
} else {
outputmd += thisNode + makeBox(thisObj.name, boxShape) + "\n";
}
// add a click link to the node
outputmd +=
"\n click " +
thisNode +
' callback "' +
thisObj.short_description +
'"\n';
for (
let j = 0;
j < configData.navigation[itemType]["children"].length;
j++
) {
let childType = configData.navigation[itemType]["children"][j];
outputmd = addTree(childType, thisObj, thisNode, outputmd);
}
}
// console.log(outputmd)
return outputmd;
}

let outputmd = "graph TB; \n";
// call the recursive addTree function, starting with the Goal as the top node
outputmd = addTree("TopLevelNormativeGoal", in_json, null, outputmd);

return outputmd;
}

updateView() {
// render() will be called again anytime setState is called, which
// is done both by hideEditLayer() and hideCreateLayer()
Expand Down Expand Up @@ -209,7 +143,6 @@ class CaseContainer extends Component {
}

showEditLayer(itemType, itemId, event) {
console.log("in showEditLayer", this, itemId);
event.preventDefault();
// this should be redundant, as the itemId and itemType should already
// be set when showViewLayer is called, but they can't do any harm..
Expand All @@ -219,7 +152,6 @@ class CaseContainer extends Component {
}

showCreateLayer(itemType, parentId, event) {
console.log("in showCreateLayer", this, parentId);
event.preventDefault();
this.setState({ createItemType: itemType, createItemParentId: parentId });
this.setState({ showCreateLayer: true });
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/components/tests/CaseContainer.test.js
Expand Up @@ -2,6 +2,8 @@ import { render, screen, waitFor } from "@testing-library/react";
import "regenerator-runtime/runtime";
import React from "react";
import "@testing-library/jest-dom";
import fetchMock from "jest-fetch-mock";

import CaseContainer from "../CaseContainer.js";

const mockedUsedNavigate = jest.fn();
Expand All @@ -10,20 +12,25 @@ jest.mock("react-router-dom", () => ({
useNavigate: () => mockedUsedNavigate,
}));

global.fetch = jest.fn(() =>
Promise.resolve({
json: () =>
Promise.resolve({ id: 1, name: "Test case", description: "", goals: [] }),
})
);
fetchMock.enableMocks();

beforeEach(() => {
fetch.resetMocks();
});

test("renders loading screen", () => {
fetch.mockResponseOnce(
JSON.stringify({ id: 1, name: "Test case", description: "", goals: [] })
);
render(<CaseContainer id="1" />);
const textElement = screen.getByText("loading");
expect(textElement).toBeInTheDocument();
});

test("renders case view", async () => {
fetch.mockResponseOnce(
JSON.stringify({ id: 1, name: "Test case", description: "", goals: [] })
);
render(<CaseContainer id="1" />);
await waitFor(() =>
expect(screen.getByDisplayValue("Test case")).toBeInTheDocument()
Expand Down

0 comments on commit 100ad18

Please sign in to comment.