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

Add preset examples #24

Merged
merged 3 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 9 additions & 6 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ErrorBoundary } from "react-error-boundary";
import DownloadSVGButton from "./DownloadSVGButton";
import { Alert } from "@mui/material";
import { configDataPropTypes } from "./MemoryModelsUserInput";
import MemoryModelsSample from "./MemoryModelsSample";

export default function App() {
const [textData, setTextData] = useState("");
Expand All @@ -18,10 +19,10 @@ export default function App() {
const [svgResult, setSvgResult] = useState(null);
const [failureBanner, setFailureBanner] = useState("");

const onSubmit = (event, data) => {
event.preventDefault();
const onTextDataSubmit = (event?) => {
event?.preventDefault();
try {
setJsonResult(JSON.parse(data));
setJsonResult(JSON.parse(textData));
setFailureBanner("");
} catch (error) {
const errorMessage = `Error parsing inputted JSON: ${error.message}`;
Expand All @@ -30,9 +31,6 @@ export default function App() {
setJsonResult(null);
}
};
const onTextDataSubmit = (event) => {
onSubmit(event, textData);
};

return (
<>
Expand All @@ -41,6 +39,11 @@ export default function App() {
{failureBanner}
</Alert>
)}
<MemoryModelsSample
setTextData={setTextData}
setConfigData={setConfigData}
onTextDataSubmit={onTextDataSubmit}
/>
<MemoryModelsUserInput
textData={textData}
setTextData={setTextData}
Expand Down
78 changes: 78 additions & 0 deletions demo/src/MemoryModelsSample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useEffect, useState } from "react";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Button,
Card,
CardContent,
Grid,
} from "@mui/material";
import { ExpandMore } from "@mui/icons-material";

import { SAMPLES } from "./sample";

type MemoryModelsSamplePropTypes = {
setTextData: React.Dispatch<React.SetStateAction<string>>;
setConfigData: React.Dispatch<React.SetStateAction<object>>;
onTextDataSubmit: () => void;
};

export default function MemoryModelsSample(props: MemoryModelsSamplePropTypes) {
const [clickedBtnIndex, setClickedBtnIndex] = useState<Number>(null);

useEffect(() => {
if (clickedBtnIndex !== null) {
props.onTextDataSubmit();
}
}, [clickedBtnIndex]);

const handleButtonClick = (index: Number, sample: Object) => {
// Note: the following conversion to a string is inefficient, as the data is later parsed
// back into JSON for rendering.
// TODO: fix this.
props.setTextData(JSON.stringify(sample["data"], null, 4));
props.setConfigData((prevConfigData) => ({
...prevConfigData,
...sample["config"],
}));
setClickedBtnIndex(index);
};

return (
<Accordion>
<AccordionSummary
expandIcon={<ExpandMore />}
data-testid="sample-inputs-accordion"
>
Sample Inputs
</AccordionSummary>
<AccordionDetails>
<Card color="neutral">
<CardContent>
<Grid container spacing={2}>
{SAMPLES.map((sample, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<Button
variant="contained"
onClick={() =>
handleButtonClick(index, sample)
}
color={
index === clickedBtnIndex
? "success"
: "primary"
}
sx={{ textTransform: "capitalize" }}
>
{sample["name"]}
</Button>
</Grid>
))}
</Grid>
</CardContent>
</Card>
</AccordionDetails>
</Accordion>
);
}
1 change: 1 addition & 0 deletions demo/src/MemoryModelsUserInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface configDataPropTypes {
useAutomation: boolean;
overallDrawConfig: {
seed: Number;
[key: string]: any;
};
}

Expand Down
69 changes: 69 additions & 0 deletions demo/src/__tests__/MemoryModelsSample.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
jest.mock(
"../sample/automated-layout/data.json",
() => ({ sample: "automation" }),
{ virtual: true }
);
jest.mock(
"../sample/automated-layout/config.json",
() => ({ config: "config" }),
{
virtual: true,
}
);

import React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import MemoryModelsSample from "../MemoryModelsSample";
import { SAMPLES } from "../sample";

describe("MemoryModelsSample", () => {
// submit button by default resets the form https://stackoverflow.com/a/62404526
const onSubmitMock = jest.fn(() => {});
const setTextDataMock = jest.fn();
let nextState;
let setConfigDataMock;

beforeEach(() => {
setConfigDataMock = jest.fn().mockImplementation((callback) => {
nextState = callback({ config: "config" });
});

render(
<MemoryModelsSample
onTextDataSubmit={onSubmitMock}
setTextData={setTextDataMock}
setConfigData={setConfigDataMock}
/>
);
});

it("renders Accordion", () => {
expect(
screen.getByTestId("sample-inputs-accordion").textContent
).toEqual("Sample Inputs");
expect(screen.getByText("Sample Inputs")).toBeDefined();
});

it("renders all sample buttons", () => {
// sx for MUI comps or non-inline CSS in general will not be loaded into Jest by default
// might be achievable with some libs but this test makes sure the base texts are present.
// Therefore, we can't test for capitalization (via sx) here
SAMPLES.map((sample) =>
expect(screen.getByText(sample["name"])).toBeDefined()
);
});

it("handles sample button click", async () => {
const button = screen.getByText("Automated Layout");
fireEvent.click(button);

// Wait for state updates and side effects to complete
await waitFor(() => {
expect(setTextDataMock).toHaveBeenCalledWith(
JSON.stringify({ sample: "automation" }, null, 4)
);
expect(nextState).toEqual({ config: "config" });
expect(onSubmitMock).toHaveBeenCalled();
});
});
});
8 changes: 1 addition & 7 deletions demo/src/__tests__/MemoryModelsUserInput.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import React from "react";
import {
fireEvent,
render,
screen,
waitFor,
within,
} from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import MemoryModelsUserInput from "../MemoryModelsUserInput";

describe("MemoryModelsUserInput", () => {
Expand Down
13 changes: 13 additions & 0 deletions demo/src/sample/automated-layout/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"useAutomation": true,
"overallDrawConfig": {
"seed": 0,
"width": 1300,
"padding": 30,
"top_margin": 30,
"bottom_margin": 40,
"left_margin": 20,
"right_margin": 30,
"sort_by": "id"
}
}
24 changes: 24 additions & 0 deletions demo/src/sample/automated-layout/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{
"isClass": true,
"name": "__main__",
"id": null,
"value": { "lst1": 82, "lst2": 84, "p": 99, "d": 10, "t": 11 },
"stack_frame": true
},
{
"isClass": true,
"name": "func",
"id": null,
"value": { "age": 12, "name": 17 },
"stack_frame": true
},
{
"isClass": false,
"name": "list",
"id": 84,
"value": [32, 10, 90, 57],
"show_indexes": true
},
{ "isClass": false, "name": "None", "id": 13, "value": "None" }
]
11 changes: 11 additions & 0 deletions demo/src/sample/blanks/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"useAutomation": true,
"overallDrawConfig": {
"seed": 0,
"width": 1300,
"padding": 30,
"right_margin": 20,
"bottom_margin": 20,
"sort_by": null
}
}
69 changes: 69 additions & 0 deletions demo/src/sample/blanks/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[
{
"isClass": true,
"name": "__main__",
"id": null,
"value": { "lst1": 82, "lst2": 84, "p": 99, "d": 10, "t": 11 },
"stack_frame": true
},
{
"name": "BLANK",
"width": 100,
"height": 200,
"stack_frame": true
},
{
"isClass": true,
"name": "func",
"id": null,
"value": { "age": 12, "name": 17 },
"stack_frame": true
},
{
"isClass": false,
"name": "list",
"id": 82,
"value": [19, 43, 28, 49]
},
{
"isClass": false,
"name": "list",
"id": 84,
"value": [32, 10, 90, 57],
"show_indexes": true
},
{
"isClass": false,
"name": "int",
"id": 19,
"value": 1969
},
{
"name": "BLANK",
"width": 100,
"height": 200
},
{
"isClass": false,
"name": "bool",
"id": 32,
"value": true
},
{
"isClass": false,
"name": "str",
"id": 43,
"value": "David is cool"
},
{
"name": "BLANK",
"width": 200,
"height": 150
},
{
"isClass": false,
"name": "tuple",
"id": 11,
"value": [82, 76]
}
]
38 changes: 38 additions & 0 deletions demo/src/sample/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import automated_layout_config from "./automated-layout/config";
import automated_layout_data from "./automated-layout/data";
import blanks_config from "./blanks/config";
import blanks_data from "./blanks/data";
import manual_layout_config from "./manual-layout/config";
import manual_layout_data from "./manual-layout/data";
import simple_config from "./simple/config";
import simple_data from "./simple/data";
import styling_config from "./styling/config";
import styling_data from "./styling/data";

export const SAMPLES = [
{
name: "Simple",
data: simple_data,
config: simple_config,
},
{
name: "Manual Layout",
data: manual_layout_data,
config: manual_layout_config,
},
{
name: "Automated Layout",
data: automated_layout_data,
config: automated_layout_config,
},
{
name: "Blank spaces",
data: blanks_data,
config: blanks_config,
},
{
name: "Custom styling",
data: styling_data,
config: styling_config,
},
];
6 changes: 6 additions & 0 deletions demo/src/sample/manual-layout/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"useAutomation": false,
"overallDrawConfig": {
"seed": 0
}
}
Loading
Loading