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 ( +
+
+ logo +
+

{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 ? + <> : +
+ + {getAlertString()} +
+ } +
+ {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