Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: simple setup vite for playground #52

Merged
merged 38 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f920e96
build: simple setup vite for playground
igorwessel Apr 11, 2024
48245fd
build: add preview script to see production build
igorwessel Apr 11, 2024
5b5c3a4
chore: remove parcel deps
igorwessel Apr 11, 2024
900014c
build: use vite to bundle react, excalidraw
igorwessel Apr 11, 2024
e8037fc
refactor: typo fix for "definition" in playground
igorwessel Apr 12, 2024
b3505c0
build: basic support hmr for test cases
igorwessel Apr 12, 2024
7b71547
build: maintain same output build format which parcel uses
igorwessel Apr 12, 2024
88c08f0
fix: hmr testcase file update not get new definition for button
igorwessel Apr 12, 2024
ece4a0d
chore: some minor adjust in tsconfig
igorwessel Apr 13, 2024
8f149ae
refactor: use glob pattern for warm-up vite
igorwessel Apr 15, 2024
390f1d0
feat: transform playground in react
igorwessel Apr 16, 2024
266a594
build: add typecheck for playground
igorwessel Apr 16, 2024
9d3d496
build: fix typecheck for playground
igorwessel Apr 16, 2024
7cbec3a
refactor: use arrow function for testcases component
igorwessel Apr 23, 2024
8155f6d
refactor: use arrow function for mermaid component
igorwessel Apr 23, 2024
9ad7db4
refactor: use arrow function for excalidraw component
igorwessel Apr 23, 2024
799c472
refactor: use arrow function for customtest component
igorwessel Apr 23, 2024
ef2a7fa
refactor: use arrow function for app component
igorwessel Apr 23, 2024
84fa718
refactor: remove context, lifted state up for app
igorwessel Apr 23, 2024
006eb29
improve and reuse types
ad1992 Apr 25, 2024
3511403
rename onChangeDefination to onChange
ad1992 Apr 25, 2024
c87ee04
cleanup Testcases.tsx
ad1992 Apr 25, 2024
694fe96
rename syntax to definition to align with testcase
ad1992 Apr 25, 2024
29710e2
fix
ad1992 Apr 25, 2024
96f6505
refactor: rename mermaid to mermaid digram
igorwessel Apr 25, 2024
192f306
refactor: remove responsability to get mermaid data in excalidraw
igorwessel Apr 25, 2024
2475856
fix: minor fixes
igorwessel Apr 25, 2024
d3716a2
refactor: rename activeTestcase to activeTestcaseIndex
igorwessel Apr 29, 2024
e540933
refactor: improve readability for testcase error prop
igorwessel Apr 29, 2024
c0752ad
refactor: separate a single testcase component in own file
igorwessel Apr 29, 2024
2e09454
refactor: move error responsability to mermaid diagram
igorwessel Apr 29, 2024
378eaf9
refactor: better way to handle active testcase file
igorwessel Apr 29, 2024
51f080c
fix: force re-render when click in button
igorwessel Apr 30, 2024
b9cfd1c
feat: show error when occurs in parseMermaid
igorwessel Apr 30, 2024
e6ee857
rename the onchange handler to handleOnChange and use explicit types
ad1992 May 1, 2024
55ac328
Refactor Single Testcase to render a single testcase
ad1992 May 1, 2024
1fab572
add missing type to class testcase
ad1992 May 1, 2024
cf65699
fix typo for flowchart testcase
igorwessel May 1, 2024
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
23 changes: 11 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@
"description": "Mermaid to Excalidraw Diagrams",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"//": "Set targets.main = false, this instructs Parcel to ignore the main parameters.",
"targets": {
"main": false,
"types": false
},
"files": [
"dist/*"
],
"type": "module",
"scripts": {
"build": "rimraf ./dist && cross-env tsc -b src",
"start": "rimraf .parcel-cache && parcel playground/index.html --open --dist-dir ./public",
"build:playground": "rimraf ./public && parcel build playground/index.html --no-scope-hoist --dist-dir ./public --public-url /",
"test:code": "eslint --max-warnings=0 --ext .js,.ts,.tsx ."
"build": "rimraf -rf ./dist && cross-env tsc -b src",
"test:code": "eslint --max-warnings=0 --ext .js,.ts,.tsx .",
"start": "vite playground",
"build:playground": "tsc --noEmit --project ./playground/tsconfig.json && vite build playground",
"preview": "npm run build:playground && vite preview --outDir ./public"
},
"dependencies": {
"@excalidraw/markdown-to-text": "0.1.2",
Expand All @@ -28,21 +24,24 @@
"@babel/core": "7.12.0",
"@excalidraw/eslint-config": "1.0.3",
"@excalidraw/excalidraw": "0.17.1-7381-cdf6d3e",
"@parcel/transformer-sass": "2.9.1",
"@types/mermaid": "9.2.0",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.4",
"@typescript-eslint/eslint-plugin": "5.59.9",
"@typescript-eslint/parser": "5.59.9",
"cross-env": "7.0.3",
"@vitejs/plugin-react-swc": "3.6.0",
"eslint": "8.42.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-prettier": "4.2.1",
"parcel": "2.9.1",
"prettier": "2.8.8",
"process": "0.11.10",
"react": "18.2.0",
"react-dom": "18.2.0",
"sass": "1.74.1",
"typescript": "5.2.2",
"rimraf": "5.0.5"
"rimraf": "5.0.5",
"vite": "5.2.8"
},
"resolutions": {
"@babel/preset-env": "7.13.8"
Expand Down
66 changes: 66 additions & 0 deletions playground/CustomTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { MermaidDiagram } from "./MermaidDiagram.tsx";
import type { MermaidData, UpdateMermaidDefinition } from "./index.tsx";

interface CustomTestProps {
onChange: UpdateMermaidDefinition;
mermaidData: MermaidData;
isActive: boolean;
}

const CustomTest = ({ onChange, mermaidData, isActive }: CustomTestProps) => {
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();

const formData = new FormData(e.target as HTMLFormElement);

onChange(formData.get("mermaid-input")?.toString() || "", true);
}}
>
<textarea
id="mermaid-input"
rows={10}
cols={50}
name="mermaid-input"
onChange={(e) => {
if (!isActive) {
return;
}

onChange(e.target.value, true);
}}
style={{ marginTop: "1rem" }}
placeholder="Input Mermaid Syntax"
/>
<br />
<button type="submit" id="render-excalidraw-btn">
{"Render to Excalidraw"}
</button>
</form>

{isActive && (
<>
<MermaidDiagram
definition={mermaidData.definition}
id="custom-diagram"
/>

<details id="parsed-data-details">
<summary>{"Parsed data from parseMermaid"}</summary>
<pre id="custom-parsed-data">
{JSON.stringify(mermaidData.output, null, 2)}
</pre>
</details>

{typeof mermaidData.error === "string" && (
<div id="error">{mermaidData.error}</div>
)}
</>
)}
</>
);
};

export default CustomTest;
59 changes: 0 additions & 59 deletions playground/ExcalidrawWrapper.ts

This file was deleted.

64 changes: 64 additions & 0 deletions playground/ExcalidrawWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useEffect, useState } from "react";
import {
Excalidraw,
convertToExcalidrawElements,
} from "@excalidraw/excalidraw";
import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types/types.js";
import { graphToExcalidraw } from "../src/graphToExcalidraw";
import { DEFAULT_FONT_SIZE } from "../src/constants";
import type { MermaidData } from "./";

interface ExcalidrawWrapperProps {
mermaidDefinition: MermaidData["definition"];
mermaidOutput: MermaidData["output"];
}

const ExcalidrawWrapper = ({
mermaidDefinition,
mermaidOutput,
}: ExcalidrawWrapperProps) => {
const [excalidrawAPI, setExcalidrawAPI] =
useState<ExcalidrawImperativeAPI | null>(null);

useEffect(() => {
if (!excalidrawAPI) {
return;
}

if (mermaidDefinition === "" || mermaidOutput === null) {
excalidrawAPI.resetScene();
return;
}

const { elements, files } = graphToExcalidraw(mermaidOutput, {
fontSize: DEFAULT_FONT_SIZE,
});

excalidrawAPI.updateScene({
elements: convertToExcalidrawElements(elements),
});
excalidrawAPI.scrollToContent(excalidrawAPI.getSceneElements(), {
fitToContent: true,
});

if (files) {
excalidrawAPI.addFiles(Object.values(files));
}
}, [mermaidDefinition, mermaidOutput]);

return (
<div className="excalidraw-wrapper">
<Excalidraw
initialData={{
appState: {
viewBackgroundColor: "#fafafa",
currentItemFontFamily: 1,
},
}}
excalidrawAPI={(api) => setExcalidrawAPI(api)}
/>
</div>
);
};

export default ExcalidrawWrapper;
31 changes: 31 additions & 0 deletions playground/MermaidDiagram.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useState, useTransition, useEffect } from "react";
import mermaid from "mermaid";

interface MermaidProps {
id: string;
definition: string;
}

export const MermaidDiagram = ({ definition, id }: MermaidProps) => {
const [svg, setSvg] = useState("");
const [, startTransition] = useTransition();

useEffect(() => {
const render = async (id: string, definition: string) => {
const { svg } = await mermaid.render(`mermaid-diagram-${id}`, definition);
startTransition(() => {
setSvg(svg);
});
};

render(id, definition);
}, [definition, id]);

return (
<div
style={{ width: "50%" }}
className="mermaid"
dangerouslySetInnerHTML={{ __html: svg }}
/>
);
};
114 changes: 114 additions & 0 deletions playground/Testcases.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useRef, useEffect, Fragment } from "react";

import { FLOWCHART_DIAGRAM_TESTCASES } from "./testcases/flowchart";
import { SEQUENCE_DIAGRAM_TESTCASES } from "./testcases/sequence.ts";
import { CLASS_DIAGRAM_TESTCASES } from "./testcases/class.ts";
import { UNSUPPORTED_DIAGRAM_TESTCASES } from "./testcases/unsupported.ts";

import type { UpdateMermaidDefinition } from "./index.tsx";
import { MermaidDiagram } from "./MermaidDiagram.tsx";

interface TestCaseProps {
name: string;
testcases: { name: string; definition: string }[];
onChange: (definition: string, activeTestcaseIndex: number) => void;
error: string | null;
activeTestcase?: number;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to activeTestCaseIndex as its the index and not the testcase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in d3716a2

}

const Testcase = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets move this component to a file SingleTestCase.tsx

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in c0752ad

name,
testcases,
onChange,
error,
activeTestcase,
}: TestCaseProps) => {
const baseId = name.toLowerCase();

useEffect(() => {
if (activeTestcase !== undefined) {
const { definition } = testcases[activeTestcase];

onChange(definition, activeTestcase);
}
}, [testcases]);

return (
<>
<h2>
{name} {"Diagrams"}
</h2>
<details>
<summary>
{name} {"Examples"}
</summary>
<div id={`${baseId}-container`} className="testcase-container">
{testcases.map(({ name, definition }, index) => {
const id = `${baseId}-${index}`;
return (
<Fragment key={id}>
<h2 style={{ marginTop: "50px", color: "#f06595" }}>{name}</h2>
<pre>{definition}</pre>
<button
onClick={() => {
onChange(definition, index);
}}
>
{"Render to Excalidraw"}
</button>

<MermaidDiagram definition={definition} id={id} />

{error && activeTestcase === index && (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would mean it shows error only for active testcase lets make it render error irrespective of the condition ?

Currently If I edit a testcase file such with errored syntax, the text gets updated however mermaid diagram doesn't rerender.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ad1992 I was thinking about this, and the idea of ​​just showing for the active test case was to not have to store the individual errors for each testcase.

Today we don't have the reference so the last error would be overwritten, so that error structure in the App file would become a kind of multidimensional array:

[
  [
    0 // index testcases files, 
    0 // index testcase inside testscases file
  ]
]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah let's not complicate it, we might add error field later in test case file itself if needed, that would simplify this.

However for now instead of not rendering anything during error, how about saying "Error in Rendering Mermaid Diagram" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ad1992 I realized that it shouldn't be the responsibility of the testcase or the custom testcase to understand if there was an error in the rendering of the memraid.

I've moved the responsibility over to MermaidDiagram.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah agreed 💯, I was considering that too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It got a lot better hahaha
done in 2e09454

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept it in the custom test case so we could track the parseMermaid, not just the diagram error.

<div id="error">{error}</div>
)}
</Fragment>
);
})}
</div>
</details>
</>
);
};

interface TestcasesProps {
onChange: UpdateMermaidDefinition;
isCustomTest: boolean;
error: TestCaseProps["error"];
}

const Testcases = ({ onChange, error, isCustomTest }: TestcasesProps) => {
const activeTestcase = useRef<[number, number]>();

const testCases = [
{ name: "Flowchart", testcases: FLOWCHART_DIAGRAM_TESTCASES },
{ name: "Sequence", testcases: SEQUENCE_DIAGRAM_TESTCASES },
{ name: "Class", testcases: CLASS_DIAGRAM_TESTCASES },
{ name: "Unsupported", testcases: UNSUPPORTED_DIAGRAM_TESTCASES },
];

return (
<>
{testCases.map(({ name, testcases }, index) => (
<Testcase
key={index}
name={name}
activeTestcase={
activeTestcase.current?.[0] === index
? activeTestcase.current[1]
: undefined
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is confusing, lets have rename to activeTestCaseIndex and only store the active test case index here.
You can instead compare it with index here and pass the prop accordingly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ad1992 now I'm a bit confused haha.
The idea was to store both the index of the test files and the index of the active testcase. Without this, it doesn't know that it's file X that has the active test, and it ends up requesting to render the Excalidraw for all files not only for current active testcase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igorwessel to fix that can we have a base index instead? It starts from 0 and keeps on incrementing until all test cases are rendered.

Copy link
Contributor Author

@igorwessel igorwessel Apr 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ad1992 I will try this approach it should work. if don't maybe we can just destructure and name these variables?

const [activeTestCaseFileIndex, activeTestCaseIndex] = activeTestCase.current

activeTestCaseFileIndex === fileIndex ? activeTestCaseIndex : undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ad1992 I change the mind of some of the responsibility and understood that the duty of triggering the active test should lie with TestCases, not with SingleTestCase
done in 378eaf9

}
onChange={(definition, activeTestcaseIndex) => {
activeTestcase.current = [index, activeTestcaseIndex];

onChange(definition, false);
}}
testcases={testcases}
error={!isCustomTest ? error : null}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
error={!isCustomTest ? error : null}
error={isCustomTest ? null : error}

better readability

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in e540933

/>
))}
</>
);
};

export default Testcases;
4 changes: 1 addition & 3 deletions playground/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
declare const ExcalidrawLib: typeof import("@excalidraw/excalidraw");
declare const React: typeof import("react");
declare const ReactDOM: typeof import("react-dom/client");
/// <reference types="vite/client" />
Loading
Loading