diff --git a/.github/workflows/deploy-pr-preview.yaml b/.github/workflows/deploy-pr-preview.yaml
index 8521dc9ed..1bf57f6f6 100644
--- a/.github/workflows/deploy-pr-preview.yaml
+++ b/.github/workflows/deploy-pr-preview.yaml
@@ -96,7 +96,20 @@ jobs:
- name: Build DOOP if changes detected
if: github.event.action != 'closed' && contains(needs.run-detect-changes.outputs.changes, 'doop')
run: |
- ./.github/scripts/build-app.sh
+ # collect the necessary information
+ entry_file=$(jq -r '.module // .main' $PACKAGE_PATH/package.json)
+ build_folder=$(dirname $entry_file)
+ package_name=$(jq -r '.name' $PACKAGE_PATH/package.json)
+
+ # Run build using turbo
+ npx turbo run build:static --filter $package_name
+
+ # Copy build folder to deploy path
+ mkdir -p "$DEPLOY_PATH/$TARGET_FOLDER"
+ cp -r "$PACKAGE_PATH/$build_folder/." "$DEPLOY_PATH/$TARGET_FOLDER"
+
+ # Generate appProps.json
+ echo "$APP_PROPS_BASE64" | base64 -d > "$DEPLOY_PATH/$TARGET_FOLDER/appProps.json"
env:
PACKAGE_PATH: apps/doop
TARGET_FOLDER: doop
diff --git a/apps/doop/.gitignore b/apps/doop/.gitignore
deleted file mode 100644
index 6aaca981d..000000000
--- a/apps/doop/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-node_modules
-
-# production
-build
-/build
-/public/build
-
-secretProps.json
-secretProps.js
-.npm/*
-
-npm-debug.log*
diff --git a/apps/doop/README.md b/apps/doop/README.md
index 12f37e242..eb451e570 100644
--- a/apps/doop/README.md
+++ b/apps/doop/README.md
@@ -4,3 +4,65 @@
[![Built with Juno](https://cloudoperators.github.io/juno/built-with-juno.svg)](https://github.com/cloudoperators/juno)
This UI offers a dashboard to aggregate all policy violations reported by the Gatekeeper instances in each cluster.
+
+# Usage
+
+## Standalone Mode
+
+To create a static, runnable build, execute the following commands:
+
+```bash
+cd apps/doop
+npx turbo build:static
+```
+
+This will generate an `index.html` file along with the necessary assets in the dist folder. You’ll need to copy a `appProps.json` file containing the required props into the dist folder.
+
+## As a Micro Frontend (MFE)
+
+To build a library version for dynamic import, use the following commands:
+
+```bash
+cd apps/doop
+npx turbo build
+```
+
+This will create a build folder with all assets. You can host this folder and load it as an MFE using dynamic import:
+
+```html
+
+
+
+```
+
+## Development Mode
+
+First, create an `appProps.json` file in the root directory of the application (apps/doop), using appProps.template.json as a reference. Customize the file with the necessary properties. Once completed, run the following commands:
+
+```bash
+cd apps/doop
+npx turbo dev
+```
+
+### Testing
+
+```bash
+cd apps/doop
+npx turbo test
+```
+
+## App Props
+
+These are the customizable application properties (appProps) that you can define in your appProps.json file:
+
+- **id** (optional): Use unique IDs if you need to instantiate multiple instances of the app on the same page.
+- **theme** (optional): Overrides the default theme. Acceptable values are `"theme-light"` or `"theme-dark"` (default).
+- **displayName** (optional): The name to be displayed in the app's header.
+- **apiEndpoint** (required): The URL of the API endpoint the app will interact with.
+- **embedded** (optional): Set to `true` if the app will be embedded within another app or page. When `true`, the app will not display the header or footer, rendering only the content. Default is `false`.
+- **isMock** (optional): Use mocked data for development purposes. Default is `false`.
+- **showDebugSeverities** (optional): Display debug severity levels in the log. Default is `false`.
diff --git a/apps/doop/__mocks__/client.js b/apps/doop/__mocks__/client.js
deleted file mode 100644
index e478dae25..000000000
--- a/apps/doop/__mocks__/client.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { JSDOM } from "jsdom"
-const dom = new JSDOM()
-global.document = dom.window.document
-global.window = dom.window
diff --git a/apps/doop/__mocks__/fileMock.js b/apps/doop/__mocks__/fileMock.js
deleted file mode 100644
index c846a2a3b..000000000
--- a/apps/doop/__mocks__/fileMock.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-module.exports = "test-file-stub"
diff --git a/apps/doop/__mocks__/styleMock.js b/apps/doop/__mocks__/styleMock.js
deleted file mode 100644
index 7aec9878d..000000000
--- a/apps/doop/__mocks__/styleMock.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-module.exports = {}
diff --git a/apps/doop/appProps.template.json b/apps/doop/appProps.template.json
new file mode 100644
index 000000000..f92daaf12
--- /dev/null
+++ b/apps/doop/appProps.template.json
@@ -0,0 +1,9 @@
+{
+ "id": "app-instance-1",
+ "theme": "theme-dark",
+ "displayName": "MyApp",
+ "apiEndpoint": "https://api.example.com/v1",
+ "embedded": false,
+ "isMock": true,
+ "showDebugSeverities": false
+}
diff --git a/apps/doop/babel.config.js b/apps/doop/babel.config.js
deleted file mode 100644
index ab7d61b5b..000000000
--- a/apps/doop/babel.config.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-module.exports = {
- env: {
- test: {
- presets: ["@babel/preset-env", "@babel/preset-react"],
- plugins: [["babel-plugin-transform-import-meta", { module: "ES6" }]],
- },
- },
-}
diff --git a/apps/doop/esbuild.config.js b/apps/doop/esbuild.config.js
deleted file mode 100644
index 6ca970e4f..000000000
--- a/apps/doop/esbuild.config.js
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-const esbuild = require("esbuild")
-const fs = require("node:fs/promises")
-const pkg = require("./package.json")
-const postcss = require("postcss")
-const sass = require("sass")
-const { transform } = require("@svgr/core")
-const url = require("postcss-url")
-// this function generates app props based on package.json and propSecrets.json
-const appProps = require("./helpers/appProps")
-
-if (!/.+\/.+\.js/.test(pkg.module)) throw new Error("module value is incorrect, use DIR/FILE.js like build/index.js")
-
-const isProduction = process.env.NODE_ENV === "production"
-// If the jspm server fails and we cannot use external packages
-// in our import map then IGNORE_EXTERNALS (global env variable)
-// should be set to true
-const IGNORE_EXTERNALS = process.env.IGNORE_EXTERNALS === "true"
-// in dev environment we prefix output file with public
-let outfile = `${isProduction ? "" : "public/"}${pkg.main || pkg.module}`
-// get output from outputfile
-let outdir = outfile.slice(0, outfile.lastIndexOf("/"))
-const args = process.argv.slice(2)
-const watch = args.indexOf("--watch") >= 0
-const serve = args.indexOf("--serve") >= 0
-
-// helpers for console log
-const green = "\x1b[32m%s\x1b[0m"
-const yellow = "\x1b[33m%s\x1b[0m"
-const clear = "\x1b"
-
-const build = async () => {
- // delete build folder and re-create it as an empty folder
- if (!isProduction) {
- await fs.rm(outdir, { recursive: true, force: true })
- await fs.mkdir(outdir, { recursive: true })
- }
- // build app
- let ctx = await esbuild.context({
- bundle: true,
- minify: isProduction,
- // target: ["es2020"],
- target: ["es2020"], //["chrome64", "firefox67", "safari11.1", "edge79"],
- format: "esm",
- platform: "browser",
- // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
- loader: { ".js": "jsx" },
- sourcemap: !isProduction,
- // here we exclude package from bundle which are defined in peerDependencies
- // our importmap generator uses also the peerDependencies to create the importmap
- // it means all packages defined in peerDependencies are in browser available via the importmap
- external: isProduction && !IGNORE_EXTERNALS ? Object.keys(pkg.peerDependencies || {}) : [],
- entryPoints: [pkg.source],
- outdir,
- // this step is important for performance reason.
- // the main file (index.js) contains minimal code needed to
- // load the app via dynamic import (splitting: true)
- splitting: true,
- // we suport only esm!
- format: "esm",
- plugins: [
- // minimal plugin to log the recompiling process.
- {
- name: "start/end",
- setup(build) {
- build.onStart(() => {
- console.log(clear)
- console.log(yellow, "Compiling...")
- })
- build.onEnd(() => console.log(green, "Done!"))
- },
- },
-
- // this custom plugin rewrites SVG imports to
- // dataurls, paths or react components based on the
- // search param and size
- {
- name: "svg-loader",
- setup(build) {
- build.onLoad(
- // consider only .svg files
- { filter: /.\.(svg)$/, namespace: "file" },
- async (args) => {
- let contents = await fs.readFile(args.path)
- // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
- let loader = "text"
- if (args.suffix === "?url") {
- // as URL
- const maxSize = 10240 // 10Kb
- // use dataurl loader for small files and file loader for big files!
- loader = contents.length <= maxSize ? "dataurl" : "file"
- } else {
- // as react component
- // use react component loader (jsx)
- loader = "jsx"
- contents = await transform(contents, {
- plugins: ["@svgr/plugin-jsx"],
- })
- }
-
- return { contents, loader }
- }
- )
- },
- },
-
- // this custom plugin rewrites image imports to
- // dataurls or urls based on the size
- {
- name: "image-loader",
- setup(build) {
- build.onLoad(
- // consider only .svg files
- { filter: /.\.(png|jpg|jpeg|gif)$/, namespace: "file" },
- async (args) => {
- let contents = await fs.readFile(args.path)
- const maxSize = 10240 // 10Kb
- // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
- // use dataurl loader for small files and file loader for big files!
- loader = contents.length <= maxSize ? "dataurl" : "file"
-
- return { contents, loader }
- }
- )
- },
- },
-
- // this custom plugin parses the style files
- {
- name: "parse-styles",
- setup(build) {
- build.onLoad(
- // consider only .scss and .css files
- { filter: /.\.(css|scss)$/, namespace: "file" },
- async (args) => {
- let content
- // handle scss, convert to css
- if (args.path.endsWith(".scss")) {
- const result = sass.renderSync({ file: args.path })
- content = result.css
- } else {
- // read file content
- content = await fs.readFile(args.path)
- }
-
- // postcss plugins
- const plugins = [
- require("tailwindcss"),
- require("autoprefixer"),
- // rewrite urls inside css
- url({
- url: "inline",
- maxSize: 10, // use dataurls if files are smaller than 10k
- fallback: "copy", // if files are bigger use copy method
- assetsPath: "./build/assets",
- useHash: true,
- optimizeSvgEncode: true,
- }),
- ]
-
- const { css } = await postcss(plugins).process(content, {
- from: args.path,
- to: outdir,
- })
- // built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
- return { contents: css, loader: "text" }
- }
- )
- },
- },
- ],
- })
-
- // watch and serve
- if (watch || serve) {
- if (watch) await ctx.watch()
- if (serve) {
- // generate app props based on package.json and secretProps.json
- await fs.writeFile(`./${outdir}/appProps.js`, `export default ${JSON.stringify(appProps())}`)
-
- let { host, port } = await ctx.serve({
- host: "0.0.0.0",
- port: parseInt(process.env.APP_PORT || process.env.PORT || 3000),
- servedir: "public",
- })
- console.log("serve on", `${host}:${port}`)
- }
- } else {
- await ctx.rebuild()
- await ctx.dispose()
- }
-}
-
-build()
diff --git a/apps/doop/helpers/appProps.js b/apps/doop/helpers/appProps.js
deleted file mode 100644
index 0bd9cf318..000000000
--- a/apps/doop/helpers/appProps.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-const path = require("path")
-const fs = require("fs")
-
-module.exports = ({ appPath = "" } = {}) => {
- const pkg = require(path.resolve(appPath, "package.json"))
- let secrets
- try {
- if (fs.existsSync(path.resolve(appPath, "secretProps.js"))) {
- secrets = require(path.resolve(appPath, "secretProps.js"))
- } else {
- secrets = require(path.resolve(appPath, "secretProps.json"))
- }
- } catch (e) {
- console.error(e)
- secrets = {}
- }
-
- const pkgAppProps = pkg.appProps || {}
- const pkgDependencyProps = pkg.appDependencies || {}
- const appProps = {}
- const dependencyProps = {}
- for (let propName in pkgAppProps) {
- // skip appDependencies
- if (propName === "appDependencies") return
- let value = pkgAppProps[propName]
- if (typeof value !== "string") value = pkgAppProps[propName].value
- appProps[propName] = value
- }
-
- // map pkg app props with the secret props
- for (let propName in secrets) {
- if (propName === "appDependencies") continue
- if (!appProps.hasOwnProperty(propName))
- throw Error(`Secret property ${propName} is not defined in package.json -> appProps`)
- appProps[propName] = secrets[propName]
- }
-
- if (secrets.appDependencies) {
- for (let propName in secrets.appDependencies) {
- if (!pkgDependencyProps.hasOwnProperty(propName))
- throw Error(`Secret property ${propName} is not defined in package.json -> appDependencies`)
- dependencyProps[propName] = secrets.appDependencies[propName]
- }
- }
-
- return { appProps, dependencyProps }
-}
diff --git a/apps/doop/index.html b/apps/doop/index.html
new file mode 100644
index 000000000..c70c09ec6
--- /dev/null
+++ b/apps/doop/index.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+ Doop
+
+
+
+
+
+
+
+
diff --git a/apps/doop/jest.config.js b/apps/doop/jest.config.js
deleted file mode 100644
index b1ae42bf1..000000000
--- a/apps/doop/jest.config.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-module.exports = {
- transform: { "\\.[jt]sx?$": "babel-jest" },
- testEnvironment: "jsdom",
- setupFilesAfterEnv: ["/setupTests.js"],
- transformIgnorePatterns: [],
- moduleNameMapper: {
- // Jest currently doesn't support resources with query parameters.
- // Therefore we add the optional query parameter matcher at the end
- // https://github.com/facebook/jest/issues/4181
- "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)(\\?.+)?$":
- require.resolve("./__mocks__/fileMock"),
- "\\.(css|less|scss)$": require.resolve("./__mocks__/styleMock"),
- },
-}
diff --git a/apps/doop/package.json b/apps/doop/package.json
index 1cddd110e..22f321a65 100644
--- a/apps/doop/package.json
+++ b/apps/doop/package.json
@@ -7,87 +7,39 @@
"Arturo Reuschenbach Pucernau"
],
"repository": "https://github.com/cloudoperators/juno/tree/main/apps/doop",
- "source": "src/index.js",
"module": "build/index.js",
"license": "Apache-2.0",
"private": true,
"devDependencies": {
- "@babel/core": "^7.20.2",
- "@babel/preset-env": "^7.20.2",
- "@babel/preset-react": "^7.18.6",
"@cloudoperators/juno-config": "*",
"@svgr/core": "^8.0.0",
"@svgr/plugin-jsx": "^8.0.0",
"@tanstack/react-query": "5.36.2",
- "@testing-library/dom": "^10.0.0",
- "@testing-library/jest-dom": "^6.0.0",
- "@testing-library/react": "^16.0.0",
- "@testing-library/user-event": "^14.4.3",
- "assert": "^2.0.0",
+ "@testing-library/react": "^16.0.1",
"autoprefixer": "^10.4.2",
- "babel-jest": "^29.3.1",
- "babel-plugin-transform-import-meta": "^2.2.0",
- "esbuild": "^0.20.1",
"interweave": "^13.1.0",
- "jest": "^29.3.1",
- "jest-environment-jsdom": "^29.3.1",
"jsdoc": "^4.0.2",
+ "jsdom": "^25.0.1",
"luxon": "^3.0.0",
"postcss": "^8.4.31",
- "postcss-url": "^10.1.3",
"prop-types": "^15.8.1",
"react-markdown": "^9.0.0",
"react-test-renderer": "^18.2.0",
"sass": "^1.77.5",
- "shadow-dom-testing-library": "^1.7.1",
+ "shadow-dom-testing-library": "^1.11.3",
"tailwindcss": "^3.3.1",
+ "vitest": "^2.1.1",
"zustand": "4.5.5"
},
"scripts": {
- "test": "jest",
- "dev": "NODE_ENV=development node esbuild.config.js --serve --watch",
- "build": "NODE_ENV=production node esbuild.config.js",
+ "test": "vitest run",
+ "dev": "vite",
+ "build": "vite build",
+ "build:static": "vite build --mode static",
+ "serve": "vite preview",
"lint": "eslint",
"clean": "rm -rf build && rm -rf node_modules && rm -rf .turbo"
},
- "appProps": {
- "id": {
- "value": "doop",
- "type": "optional",
- "description": "If you want to instantiate more than one app per page then use different ids"
- },
- "theme": {
- "value": "theme-dark",
- "type": "optional",
- "description": "Override the default theme. Possible values are theme-light or theme-dark (default)"
- },
- "displayName": {
- "value": "Global",
- "description": "Name to use in UI",
- "type": "optional"
- },
- "apiEndpoint": {
- "value": "",
- "type": "required",
- "description": "Endpoint URL of the Global Doop API"
- },
- "embedded": {
- "value": "true",
- "type": "optional",
- "description": "Set to true if app is to be embedded in another existing app or page. If set to true the app won't render a page header/footer and instead render only the content. The default value is false."
- },
- "isMock": {
- "value": "false",
- "type": "optional",
- "description": "Use mocked data (only in dev)"
- },
- "showDebugSeverities": {
- "value": "false",
- "type": "optional",
- "description": "Show debug severities in the log"
- }
- },
- "appPreview": true,
"dependencies": {
"@cloudoperators/juno-communicator": "*",
"@cloudoperators/juno-messages-provider": "*",
diff --git a/apps/doop/public/index.html b/apps/doop/public/index.html
deleted file mode 100644
index 55af0cbed..000000000
--- a/apps/doop/public/index.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- doop Dev
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/doop/secretProps.template.json b/apps/doop/secretProps.template.json
deleted file mode 100644
index c463552e5..000000000
--- a/apps/doop/secretProps.template.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "theme": "theme-dark",
- "endpoint": "https://endpoint/api/v1"
-}
\ No newline at end of file
diff --git a/apps/doop/setupTests.js b/apps/doop/setupTests.js
deleted file mode 100644
index bc62a34b6..000000000
--- a/apps/doop/setupTests.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import "@testing-library/jest-dom"
diff --git a/apps/doop/src/App.jsx b/apps/doop/src/App.jsx
index fab8bb1fc..985736399 100644
--- a/apps/doop/src/App.jsx
+++ b/apps/doop/src/App.jsx
@@ -7,7 +7,7 @@ import React, { useEffect, useMemo, useLayoutEffect } from "react"
import { AppShellProvider, ContentHeading } from "@cloudoperators/juno-ui-components"
import AppContent from "./components/AppContent"
-import styles from "./styles.scss"
+import styles from "./styles.module.scss"
import AuthProvider from "./components/AuthProvider"
import { MessagesProvider } from "@cloudoperators/juno-messages-provider"
import StoreProvider from "./components/StoreProvider"
@@ -15,7 +15,7 @@ import AsyncWorker from "./components/AsyncWorker"
import { AppShell } from "@cloudoperators/juno-ui-components"
import { QueryClientProvider, QueryClient } from "@tanstack/react-query"
import { fetchProxyInitDB } from "@cloudoperators/juno-utils"
-import db from "../db.json"
+import db from "./db.json"
import { useGlobalsActions } from "./components/StoreProvider"
const App = (props = {}) => {
diff --git a/apps/doop/src/App.test.js b/apps/doop/src/App.test.js
deleted file mode 100644
index 6f50c58fc..000000000
--- a/apps/doop/src/App.test.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React from "react"
-import { render, act } from "@testing-library/react"
-// support shadow dom queries
-// https://reactjsexample.com/an-extension-of-dom-testing-library-to-provide-hooks-into-the-shadow-dom/
-import { screen } from "shadow-dom-testing-library"
-import App from "./App"
-
-// Mock BroadcastChannel
-globalThis.BroadcastChannel = jest.fn(() => ({
- postMessage: jest.fn(),
- close: jest.fn(),
-}))
-
-test("renders app", async () => {
- await act(() => render())
-
- let loginTitle = await screen.queryAllByShadowText(/DOOP/i)
-
- expect(loginTitle.length > 0).toBe(true)
-})
diff --git a/apps/doop/src/App.test.jsx b/apps/doop/src/App.test.jsx
new file mode 100644
index 000000000..e8821da79
--- /dev/null
+++ b/apps/doop/src/App.test.jsx
@@ -0,0 +1,29 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from "react"
+import { render } from "@testing-library/react"
+// support shadow dom queries
+// https://reactjsexample.com/an-extension-of-dom-testing-library-to-provide-hooks-into-the-shadow-dom/
+import { screen } from "shadow-dom-testing-library"
+import App from "./App"
+import { describe } from "node:test"
+
+// Mock the styles
+vi.mock("./styles.module.scss", () => ({
+ default: new Proxy(new Object(), {
+ toString() {
+ return "/*TEST STYLES*/"
+ },
+ }),
+}))
+
+describe("App", () => {
+ it("should render the App component", () => {
+ render()
+ const loginTitle = screen.queryAllByShadowText(/DOOP/i)
+ expect(loginTitle.length > 0).toBe(true)
+ })
+})
diff --git a/apps/doop/src/assets/juno-danger.svg b/apps/doop/src/assets/juno-danger.svg
deleted file mode 100644
index 495d4d1b9..000000000
--- a/apps/doop/src/assets/juno-danger.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/apps/doop/src/assets/map.svg b/apps/doop/src/assets/map.svg
deleted file mode 100644
index b4fdaa72d..000000000
--- a/apps/doop/src/assets/map.svg
+++ /dev/null
@@ -1,4421 +0,0 @@
-
-
-
-
-
diff --git a/apps/doop/src/assets/rocket.gif b/apps/doop/src/assets/rocket.gif
deleted file mode 100644
index 999d3d3bf..000000000
Binary files a/apps/doop/src/assets/rocket.gif and /dev/null differ
diff --git a/apps/doop/db.json b/apps/doop/src/db.json
similarity index 100%
rename from apps/doop/db.json
rename to apps/doop/src/db.json
diff --git a/apps/doop/src/hooks/useCommunication.js b/apps/doop/src/hooks/useCommunication.js
index 842ef8963..2e6162094 100644
--- a/apps/doop/src/hooks/useCommunication.js
+++ b/apps/doop/src/hooks/useCommunication.js
@@ -32,7 +32,7 @@ const useCommunication = () => {
const unwatch = watch(
"USER_ACTIVITY_UPDATE_DATA",
(data) => {
- console.log("got message USER_ACTIVITY_UPDATE_DATA: ", data)
+ console.debug("got message USER_ACTIVITY_UPDATE_DATA: ", data)
setIsActive(data?.isActive)
},
{ debug: true, consumerID: "doop" }
diff --git a/apps/doop/src/hooks/useUrlState.js b/apps/doop/src/hooks/useUrlState.js
index 69f0aa8cf..ef339ead8 100644
--- a/apps/doop/src/hooks/useUrlState.js
+++ b/apps/doop/src/hooks/useUrlState.js
@@ -39,7 +39,7 @@ const useUrlState = (key) => {
useEffect(() => {
// don't read the url if we are already reading it or if we are not logged in
if (isURLRead || !loggedIn) return
- console.log(`DOOP: (${key || DEFAULT_KEY}) setting up state from url:`, urlStateManager.currentState())
+ console.debug(`DOOP: (${key || DEFAULT_KEY}) setting up state from url:`, urlStateManager.currentState())
const searchTerm = urlStateManager.currentState()?.[SEARCH_TERM]
const activeFilters = urlStateManager.currentState()?.[ACTIVE_FILTERS]
diff --git a/apps/doop/public/favicon-16x16.png b/apps/doop/src/img/favicon-16x16.png
similarity index 100%
rename from apps/doop/public/favicon-16x16.png
rename to apps/doop/src/img/favicon-16x16.png
diff --git a/apps/doop/public/favicon-32x32.png b/apps/doop/src/img/favicon-32x32.png
similarity index 100%
rename from apps/doop/public/favicon-32x32.png
rename to apps/doop/src/img/favicon-32x32.png
diff --git a/apps/doop/public/favicon.ico b/apps/doop/src/img/favicon.ico
similarity index 100%
rename from apps/doop/public/favicon.ico
rename to apps/doop/src/img/favicon.ico
diff --git a/apps/doop/src/lib/store/createAuthDataSlice.js b/apps/doop/src/lib/store/createAuthDataSlice.jsx
similarity index 100%
rename from apps/doop/src/lib/store/createAuthDataSlice.js
rename to apps/doop/src/lib/store/createAuthDataSlice.jsx
diff --git a/apps/doop/src/lib/store/createDataSlice.js b/apps/doop/src/lib/store/createDataSlice.jsx
similarity index 100%
rename from apps/doop/src/lib/store/createDataSlice.js
rename to apps/doop/src/lib/store/createDataSlice.jsx
diff --git a/apps/doop/src/lib/store/createDataSlice.test.js b/apps/doop/src/lib/store/createDataSlice.test.jsx
similarity index 99%
rename from apps/doop/src/lib/store/createDataSlice.test.js
rename to apps/doop/src/lib/store/createDataSlice.test.jsx
index d2440e41e..021ec6c28 100644
--- a/apps/doop/src/lib/store/createDataSlice.test.js
+++ b/apps/doop/src/lib/store/createDataSlice.test.jsx
@@ -13,7 +13,7 @@ import StoreProvider, {
useDataClusterIdentities,
} from "../../components/StoreProvider.jsx"
-import data from "../../../db.json"
+import data from "../../db.json"
describe("createDataSlice", () => {
describe("setData", () => {
diff --git a/apps/doop/src/lib/store/createFiltersSlice.js b/apps/doop/src/lib/store/createFiltersSlice.jsx
similarity index 100%
rename from apps/doop/src/lib/store/createFiltersSlice.js
rename to apps/doop/src/lib/store/createFiltersSlice.jsx
diff --git a/apps/doop/src/lib/store/createFiltersSlice.test.js b/apps/doop/src/lib/store/createFiltersSlice.test.jsx
similarity index 99%
rename from apps/doop/src/lib/store/createFiltersSlice.test.js
rename to apps/doop/src/lib/store/createFiltersSlice.test.jsx
index daed0a3a9..d7fe3763b 100644
--- a/apps/doop/src/lib/store/createFiltersSlice.test.js
+++ b/apps/doop/src/lib/store/createFiltersSlice.test.jsx
@@ -6,7 +6,8 @@
import * as React from "react"
import { renderHook, act } from "@testing-library/react"
import StoreProvider, { useDataActions, useFiltersActions, useDataFilteredItems } from "../../components/StoreProvider"
-import data from "../../../db.json"
+
+import data from "../../db.json"
describe("createFiltersSlice", () => {
describe("set", () => {
diff --git a/apps/doop/src/lib/store/createGlobalsSlice.js b/apps/doop/src/lib/store/createGlobalsSlice.jsx
similarity index 100%
rename from apps/doop/src/lib/store/createGlobalsSlice.js
rename to apps/doop/src/lib/store/createGlobalsSlice.jsx
diff --git a/apps/doop/src/lib/store/createUserActivitySlice.js b/apps/doop/src/lib/store/createUserActivitySlice.jsx
similarity index 100%
rename from apps/doop/src/lib/store/createUserActivitySlice.js
rename to apps/doop/src/lib/store/createUserActivitySlice.jsx
diff --git a/apps/doop/src/styles.scss b/apps/doop/src/styles.module.scss
similarity index 77%
rename from apps/doop/src/styles.scss
rename to apps/doop/src/styles.module.scss
index 7aee5657f..cebb6de1d 100644
--- a/apps/doop/src/styles.scss
+++ b/apps/doop/src/styles.module.scss
@@ -6,18 +6,6 @@
@tailwind components;
@tailwind utilities;
-
-/* If necessary, app styles can be added below */
-
-.svg-bg-test {
- background: url('assets/juno-danger.svg')
-}
-
-// .svg-bg-test-big-file {
-// background: left 80px no-repeat url('assets/map.svg')
-// }
-
-
.info-box {
h4 {
font-size: 1.2rem;
@@ -30,7 +18,6 @@
}
}
-
// datagrid row hover style
// REMOVE THIS ONCE DATAGRID COMPONENT SUPPORTS HOVER
.violations-list .juno-datagrid-row:hover {
@@ -44,4 +31,3 @@
@apply bg-theme-background-lvl-2;
}
}
-
diff --git a/apps/doop/turbo.json b/apps/doop/turbo.json
index fcfeaa2b7..26f1bd3d4 100644
--- a/apps/doop/turbo.json
+++ b/apps/doop/turbo.json
@@ -16,6 +16,14 @@
"@cloudoperators/juno-messages-provider#build",
"@cloudoperators/juno-communicator#build"
]
+ },
+ "build:static": {
+ "dependsOn": [
+ "@cloudoperators/juno-ui-components#build",
+ "@cloudoperators/juno-utils#build",
+ "@cloudoperators/juno-messages-provider#build",
+ "@cloudoperators/juno-communicator#build"
+ ]
}
}
}
diff --git a/apps/doop/vite.config.ts b/apps/doop/vite.config.ts
new file mode 100644
index 000000000..74360cbf0
--- /dev/null
+++ b/apps/doop/vite.config.ts
@@ -0,0 +1,53 @@
+import { defineConfig } from "vite"
+import react from "@vitejs/plugin-react"
+import tailwindcss from "tailwindcss"
+import autoprefixer from "autoprefixer"
+
+export default defineConfig(({ mode }) => {
+ const sharedConfig = {
+ root: "./",
+
+ define: {
+ "process.env": {},
+ },
+
+ plugins: [react()],
+ css: {
+ postcss: {
+ plugins: [tailwindcss, autoprefixer],
+ },
+ },
+
+ server: {
+ host: "0.0.0.0",
+ port: parseInt(process.env.PORT || "3000"),
+ },
+ }
+
+ // with vite it is possible to have different configurations based on the mode
+ // we can use this to create a static build for previewing the app in github pages
+ // and also to create a docker image for the standalone app
+ if (mode === "static") {
+ return {
+ ...sharedConfig,
+ base: "./", // Relative Path in Generated index.html
+ build: {
+ outDir: "build",
+ },
+ }
+ }
+
+ // Default is a library
+ return {
+ ...sharedConfig,
+ build: {
+ outDir: "build",
+
+ lib: {
+ entry: "src/index.js",
+ formats: ["es"],
+ fileName: (format) => `index.js`,
+ },
+ },
+ }
+})
diff --git a/apps/doop/vitest.config.ts b/apps/doop/vitest.config.ts
new file mode 100644
index 000000000..5fbf7036f
--- /dev/null
+++ b/apps/doop/vitest.config.ts
@@ -0,0 +1,15 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { defineConfig } from "vitest/config"
+
+export default defineConfig({
+ test: {
+ globals: true,
+ environment: "jsdom",
+ setupFiles: "./vitest.setup.ts",
+ watch: true,
+ },
+})
diff --git a/apps/doop/vitest.setup.ts b/apps/doop/vitest.setup.ts
new file mode 100644
index 000000000..64c5b19e3
--- /dev/null
+++ b/apps/doop/vitest.setup.ts
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { beforeAll } from "vitest"
+import { expect } from "vitest"
+import matchers from "@testing-library/jest-dom/matchers"
+
+expect.extend(matchers)
+
+beforeAll(() => {
+ // Mock global objects if necessary
+ global.window = window
+ global.document = window.document
+})
diff --git a/package-lock.json b/package-lock.json
index 2083fffe0..ec0d51324 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,35 +40,24 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
- "@babel/core": "^7.20.2",
- "@babel/preset-env": "^7.20.2",
- "@babel/preset-react": "^7.18.6",
"@cloudoperators/juno-config": "*",
"@svgr/core": "^8.0.0",
"@svgr/plugin-jsx": "^8.0.0",
"@tanstack/react-query": "5.36.2",
- "@testing-library/dom": "^10.0.0",
- "@testing-library/jest-dom": "^6.0.0",
- "@testing-library/react": "^16.0.0",
- "@testing-library/user-event": "^14.4.3",
- "assert": "^2.0.0",
+ "@testing-library/react": "^16.0.1",
"autoprefixer": "^10.4.2",
- "babel-jest": "^29.3.1",
- "babel-plugin-transform-import-meta": "^2.2.0",
- "esbuild": "^0.20.1",
"interweave": "^13.1.0",
- "jest": "^29.3.1",
- "jest-environment-jsdom": "^29.3.1",
"jsdoc": "^4.0.2",
+ "jsdom": "^25.0.1",
"luxon": "^3.0.0",
"postcss": "^8.4.31",
- "postcss-url": "^10.1.3",
"prop-types": "^15.8.1",
"react-markdown": "^9.0.0",
"react-test-renderer": "^18.2.0",
"sass": "^1.77.5",
- "shadow-dom-testing-library": "^1.7.1",
+ "shadow-dom-testing-library": "^1.11.3",
"tailwindcss": "^3.3.1",
+ "vitest": "^2.1.1",
"zustand": "4.5.5"
}
},
@@ -139,6 +128,7 @@
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -153,48 +143,6 @@
"node": ">=18"
}
},
- "apps/doop/node_modules/@testing-library/jest-dom": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz",
- "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@adobe/css-tools": "^4.4.0",
- "aria-query": "^5.0.0",
- "chalk": "^3.0.0",
- "css.escape": "^1.5.1",
- "dom-accessibility-api": "^0.6.3",
- "lodash": "^4.17.21",
- "redent": "^3.0.0"
- },
- "engines": {
- "node": ">=14",
- "npm": ">=6",
- "yarn": ">=1"
- }
- },
- "apps/doop/node_modules/@testing-library/jest-dom/node_modules/chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "apps/doop/node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
- "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
- "dev": true,
- "license": "MIT"
- },
"apps/doop/node_modules/@testing-library/react": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz",
@@ -223,6 +171,58 @@
}
}
},
+ "apps/doop/node_modules/jsdom": {
+ "version": "25.0.1",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
+ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==",
+ "dev": true,
+ "dependencies": {
+ "cssstyle": "^4.1.0",
+ "data-urls": "^5.0.0",
+ "decimal.js": "^10.4.3",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.5",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.12",
+ "parse5": "^7.1.2",
+ "rrweb-cssom": "^0.7.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^5.0.0",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0",
+ "ws": "^8.18.0",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "canvas": "^2.11.2"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "apps/doop/node_modules/tough-cookie": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
+ "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
+ "dev": true,
+ "dependencies": {
+ "tldts": "^6.1.32"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"apps/example": {
"name": "@cloudoperators/juno-app-example",
"version": "1.0.10",