diff --git a/web/playground/package.json b/web/playground/package.json
index e73d737..35220eb 100644
--- a/web/playground/package.json
+++ b/web/playground/package.json
@@ -2,7 +2,7 @@
"name": "playground",
"version": "0.1.0",
"private": true,
- "homepage": "./web",
+ "homepage": "/web",
"dependencies": {
"@monaco-editor/react": "^4.6.0",
"@testing-library/jest-dom": "^5.17.0",
diff --git a/web/playground/src/App.js b/web/playground/src/App.js
index 56e2b1f..c361da8 100644
--- a/web/playground/src/App.js
+++ b/web/playground/src/App.js
@@ -1,4 +1,4 @@
-import logo from "./logo.png";
+import logo from "./images/logo.png";
import "./App.scss";
import TemplateDialog from "./components/templateDialog/TemplateDialog";
import { useNavigate } from "react-router-dom";
diff --git a/web/playground/src/components/errorBoundary/ErrorBoundary.js b/web/playground/src/components/errorBoundary/ErrorBoundary.js
new file mode 100644
index 0000000..6c6f9e3
--- /dev/null
+++ b/web/playground/src/components/errorBoundary/ErrorBoundary.js
@@ -0,0 +1,8 @@
+// ErrorBoundary component can't be created as a function, but since
+// we're using this component only to display a navigation error in
+// react router it's fine as it is
+function ErrorBoundary({ fallback }) {
+ return fallback;
+}
+
+export default ErrorBoundary;
\ No newline at end of file
diff --git a/web/playground/src/components/placeholder/Placeholder.js b/web/playground/src/components/placeholder/Placeholder.js
new file mode 100644
index 0000000..d196d24
--- /dev/null
+++ b/web/playground/src/components/placeholder/Placeholder.js
@@ -0,0 +1,37 @@
+import "./Placeholder.scss";
+import seeNoEvilImg from "../../images/see-no-evil-monkey.png";
+import Button from "react-bootstrap/Button";
+
+/**
+ *
+ * @param {Object} props
+ * @param {"page"} props.type
+ * @param {String} props.title
+ * @param {String} props.description
+ */
+function Placeholder({ type, title, description }) {
+ const goBack = () => {
+ window.history.go(-1);
+ };
+
+ return (
+
+
+

+
+
{title}
+
{description}
+
+
+
+
+
+
+ );
+}
+
+export default Placeholder;
diff --git a/web/playground/src/components/placeholder/Placeholder.scss b/web/playground/src/components/placeholder/Placeholder.scss
new file mode 100644
index 0000000..93c1574
--- /dev/null
+++ b/web/playground/src/components/placeholder/Placeholder.scss
@@ -0,0 +1,13 @@
+.page-placeholder {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.placeholder-image {
+ height: 20vmin;
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/web/playground/src/components/placeholder/Placeholder.test.js b/web/playground/src/components/placeholder/Placeholder.test.js
new file mode 100644
index 0000000..385cdee
--- /dev/null
+++ b/web/playground/src/components/placeholder/Placeholder.test.js
@@ -0,0 +1,16 @@
+import * as React from "react";
+import { render, screen } from "@testing-library/react";
+import "@testing-library/jest-dom";
+import Placeholder from "./Placeholder";
+
+const mockedHandleEditorChange = jest.fn();
+
+describe("Placeholder", () => {
+ it("Renders Placeholder component", async () => {
+ render();
+
+ // ensure monaco editor loads correctly
+ expect(await screen.findByText("Lorem")).toBeInTheDocument();
+ expect(await screen.findByText("Ipsum")).toBeInTheDocument();
+ });
+});
diff --git a/web/playground/src/logo.png b/web/playground/src/images/logo.png
similarity index 100%
rename from web/playground/src/logo.png
rename to web/playground/src/images/logo.png
diff --git a/web/playground/src/images/see-no-evil-monkey.png b/web/playground/src/images/see-no-evil-monkey.png
new file mode 100644
index 0000000..cb9cc3f
Binary files /dev/null and b/web/playground/src/images/see-no-evil-monkey.png differ
diff --git a/web/playground/src/index.js b/web/playground/src/index.js
index 720e1cf..f43bb75 100644
--- a/web/playground/src/index.js
+++ b/web/playground/src/index.js
@@ -8,17 +8,34 @@ import "./index.scss";
import App from "./App";
import Playground from "./screens/playground/Playground";
import { Configuration } from "./Configuration.js";
+import ErrorBoundary from "./components/errorBoundary/ErrorBoundary";
+import Placeholder from "./components/placeholder/Placeholder";
const url = Configuration.path;
+const getErrorElement = () => {
+ return (
+
+ }
+ />
+ );
+};
+
const router = createBrowserRouter([
{
path: `${url}`,
element: ,
+ errorElement: getErrorElement()
},
{
path: `${url}:playgroundId`,
- element:
+ element: ,
+ errorElement: getErrorElement()
}
]);
diff --git a/web/playground/src/screens/playground/Playground.js b/web/playground/src/screens/playground/Playground.js
index 5b4d2ae..b778a81 100644
--- a/web/playground/src/screens/playground/Playground.js
+++ b/web/playground/src/screens/playground/Playground.js
@@ -1,5 +1,6 @@
import Editor from "../../components/editor/Editor";
import Shell from "../../components/shell/Shell";
+import Placeholder from "../../components/placeholder/Placeholder";
import { useState, useMemo, useEffect } from "react";
import { useParams, useLocation } from "react-router-dom";
import { getPlaygroundHistory } from "../../services/PlaygroundService";
@@ -14,6 +15,7 @@ function Playground() {
const [program, setProgram] = useState("");
const [history, setHistory] = useState([]);
const [playgroundNotFound, setPlaygroundNotFound] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
const { sendMessage, lastMessage } = useWebSocket(WEBSOCKET_URL);
@@ -32,6 +34,7 @@ function Playground() {
useEffect(() => {
(async () => {
if (state?.skipFetchHistory) {
+ setIsLoading(false);
return;
}
@@ -42,6 +45,8 @@ function Playground() {
} catch (error) {
setPlaygroundNotFound(true);
return;
+ } finally {
+ setIsLoading(false);
}
})();
return () => {
@@ -78,31 +83,37 @@ function Playground() {
}
};
- if (playgroundNotFound) {
- // todo: improve this screen
- return (
- 404 playground wasn't found
- );
+ if (isLoading) {
+ return (<>>);
}
+
return (
<>
-
-
- {getAlertString()}
-
-
-
-
-
-
-
-
+ {playgroundNotFound ?
+ <>> :
+
+ }
+
+ {playgroundNotFound ?
+
:
+ <>
+
+
+
+
+
+
+ >
+ }
>
);
diff --git a/web/playground/src/screens/playground/Playground.test.js b/web/playground/src/screens/playground/Playground.test.js
index 2cd1249..c4c02c2 100644
--- a/web/playground/src/screens/playground/Playground.test.js
+++ b/web/playground/src/screens/playground/Playground.test.js
@@ -13,8 +13,16 @@ jest.mock("platform-detect", () => {
macos: true,
};
});
+jest.mock('../../services/PlaygroundService.js', () => {
+ const originalModule = jest.requireActual('../../services/PlaygroundService.js');
+ return {
+ __esModule: true,
+ ...originalModule,
+ getPlaygroundHistory: () => jest.fn(() => [{ program: '', history: [] }])
+ };
+});
describe("Playground", () => {
- it("renders Playground component", () => {
+ it("renders Playground component", async () => {
render(
@@ -22,6 +30,6 @@ describe("Playground", () => {
);
// ensure playground is in the document and enabled
- expect(screen.getByTestId("playground-screen")).toBeEnabled();
+ expect(await screen.findByTestId("playground-screen")).toBeEnabled();
});
});
\ No newline at end of file