From 7e44f9c4d9b39131fa3cd7435893b9a8d59f95ca Mon Sep 17 00:00:00 2001 From: I531348 Date: Wed, 22 Jan 2025 09:28:45 +0100 Subject: [PATCH 1/9] chore(ui): convert components to typescript syntax --- apps/example/src/components/AsyncWorker.tsx | 13 ++++++------ apps/example/src/components/ModalManager.tsx | 3 ++- apps/example/src/components/Playground.tsx | 2 +- apps/example/src/components/WelcomeView.tsx | 2 +- apps/example/src/components/auth/Avatar.tsx | 13 ++++++------ apps/example/src/components/peaks/Peaks.tsx | 21 +++++++++++++++---- .../src/components/shared/HintLoading.tsx | 12 +++++------ .../src/components/shared/HintNotFound.tsx | 11 +++++----- 8 files changed, 45 insertions(+), 32 deletions(-) diff --git a/apps/example/src/components/AsyncWorker.tsx b/apps/example/src/components/AsyncWorker.tsx index de79916ac..079e393b6 100644 --- a/apps/example/src/components/AsyncWorker.tsx +++ b/apps/example/src/components/AsyncWorker.tsx @@ -3,18 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -import PropTypes from "prop-types" +import React from "react" + import useQueryClientFn from "../hooks/useQueryClientFn" import useUrlState from "../hooks/useUrlState" -const AsyncWorker = ({ consumerId }: any) => { +interface AsyncWorkerProps { + consumerId: string +} + +const AsyncWorker: React.FC = ({ consumerId }) => { useQueryClientFn() useUrlState(consumerId) return null } -AsyncWorker.propTypes = { - consumerId: PropTypes.string.isRequired, -} - export default AsyncWorker diff --git a/apps/example/src/components/ModalManager.tsx b/apps/example/src/components/ModalManager.tsx index 02b8e9e5d..f097fcabb 100644 --- a/apps/example/src/components/ModalManager.tsx +++ b/apps/example/src/components/ModalManager.tsx @@ -4,10 +4,11 @@ */ import React from "react" + import { useGlobalsCurrentModal } from "./StoreProvider" import TestModal from "./TestModal" -const ModalManager = () => { +const ModalManager: React.FC = () => { const currentModal = useGlobalsCurrentModal() switch (currentModal) { diff --git a/apps/example/src/components/Playground.tsx b/apps/example/src/components/Playground.tsx index f9d6a3e9b..d95bbb6bd 100644 --- a/apps/example/src/components/Playground.tsx +++ b/apps/example/src/components/Playground.tsx @@ -7,7 +7,7 @@ import React from "react" import { ComboBox, Form, FormRow, Select, SelectOption } from "@cloudoperators/juno-ui-components" /** A component to include anywhere in the example app, just to try out things and validate behaviour in an app environment */ -const Playground = () => { +const Playground: React.FC = () => { return (
diff --git a/apps/example/src/components/WelcomeView.tsx b/apps/example/src/components/WelcomeView.tsx index 3e2cfea3e..6fe08d438 100644 --- a/apps/example/src/components/WelcomeView.tsx +++ b/apps/example/src/components/WelcomeView.tsx @@ -6,7 +6,7 @@ import React from "react" import { Stack } from "@cloudoperators/juno-ui-components" -const WelcomeView = () => { +const WelcomeView: React.FC = () => { return (

diff --git a/apps/example/src/components/auth/Avatar.tsx b/apps/example/src/components/auth/Avatar.tsx index b28a6754f..b141181b1 100644 --- a/apps/example/src/components/auth/Avatar.tsx +++ b/apps/example/src/components/auth/Avatar.tsx @@ -4,7 +4,6 @@ */ import React from "react" -import PropTypes from "prop-types" import { Stack } from "@cloudoperators/juno-ui-components" const avatarCss = ` @@ -15,7 +14,12 @@ rounded-full bg-cover ` -const Avatar = ({ userName, url }: any) => { +interface AvatarProps { + userName?: string + url?: string +} + +const Avatar: React.FC = ({ userName, url }) => { return ( {url && ( @@ -32,9 +36,4 @@ const Avatar = ({ userName, url }: any) => { ) } -Avatar.propTypes = { - userName: PropTypes.string, - url: PropTypes.string, -} - export default Avatar diff --git a/apps/example/src/components/peaks/Peaks.tsx b/apps/example/src/components/peaks/Peaks.tsx index b32a199c7..ed7fe8e84 100644 --- a/apps/example/src/components/peaks/Peaks.tsx +++ b/apps/example/src/components/peaks/Peaks.tsx @@ -5,22 +5,35 @@ import React from "react" import { useQuery } from "@tanstack/react-query" -import PeaksList from "./PeaksList" import { Spinner, Message } from "@cloudoperators/juno-ui-components" + import { useGlobalsQueryClientFnReady } from "../StoreProvider" +import PeaksList from "./PeaksList" + +interface Error { + statusCode?: number + message?: string +} + +interface Peak { + id: number + name: string + height: number +} -const Peaks = () => { +const Peaks: React.FC = () => { const queryClientFnReady = useGlobalsQueryClientFnReady() - const { isLoading, isError, data, error } = useQuery({ + const { isLoading, isError, data, error } = useQuery({ queryKey: [`peaks`], + queryFn: () => fetch("/api/peaks").then((res) => res.json()), // Replace with actual data fetching. enabled: !!queryClientFnReady, }) return ( <> {isError && ( - {`${error.statusCode ? `${error.statusCode}, ` : ""}${error?.message}`} + {`${error?.statusCode ? `${error.statusCode}, ` : ""}${error?.message}`} )} {/* Loading indicator for page content */} {isLoading && } diff --git a/apps/example/src/components/shared/HintLoading.tsx b/apps/example/src/components/shared/HintLoading.tsx index 28c0cd26e..d52724db0 100644 --- a/apps/example/src/components/shared/HintLoading.tsx +++ b/apps/example/src/components/shared/HintLoading.tsx @@ -4,19 +4,19 @@ */ import React from "react" -import PropTypes from "prop-types" import { Stack, Spinner } from "@cloudoperators/juno-ui-components" -const HintLoading = ({ text }: any) => { +interface HintLoadingProps { + text?: string +} + +const HintLoading: React.FC = ({ text }) => { return ( - {text ? {text} : Loading...} + {text ? text : "Loading..."} ) } -HintLoading.propTypes = { - text: PropTypes.string, -} export default HintLoading diff --git a/apps/example/src/components/shared/HintNotFound.tsx b/apps/example/src/components/shared/HintNotFound.tsx index 4b1a95326..2e374745a 100644 --- a/apps/example/src/components/shared/HintNotFound.tsx +++ b/apps/example/src/components/shared/HintNotFound.tsx @@ -4,10 +4,13 @@ */ import React from "react" -import PropTypes from "prop-types" import { Stack } from "@cloudoperators/juno-ui-components" -const HintNotFound = ({ text }: any) => { +interface HintNotFoundProps { + text?: string +} + +const HintNotFound: React.FC = ({ text }) => { return ( {text || "No items found"} @@ -15,8 +18,4 @@ const HintNotFound = ({ text }: any) => { ) } -HintNotFound.propTypes = { - text: PropTypes.string, -} - export default HintNotFound From 67ebb02e5d0f6ab16a56664e69083f59ac873ec0 Mon Sep 17 00:00:00 2001 From: I531348 Date: Mon, 27 Jan 2025 08:33:22 +0100 Subject: [PATCH 2/9] chore(ui): improve types --- apps/example/src/App.test.tsx | 15 +-- apps/example/src/App.tsx | 84 +++++------- apps/example/src/components/AppContent.tsx | 31 ++--- .../src/components/MonorepoChecker.tsx | 5 +- apps/example/src/components/PanelManager.tsx | 24 ++-- apps/example/src/components/StoreProvider.tsx | 101 +++++++++----- apps/example/src/components/TestModal.tsx | 9 +- apps/example/src/components/auth/Avatar.tsx | 4 +- .../src/components/auth/HeaderUser.tsx | 24 ++-- apps/example/src/components/peaks/Peaks.tsx | 23 +++- .../src/components/peaks/PeaksEdit.tsx | 127 ++++++++++-------- .../src/components/peaks/PeaksList.tsx | 73 +++++----- .../src/components/peaks/PeaksListItem.tsx | 55 +++----- .../example/src/components/peaks/PeaksNew.tsx | 111 ++++++++------- apps/example/src/hooks/useQueryClientFn.ts | 43 +++--- apps/example/src/hooks/useUrlState.ts | 26 ++-- apps/example/src/index.tsx | 56 +++++--- apps/example/src/lib/store/createAuthSlice.ts | 38 ++++-- .../src/lib/store/createGlobalsSlice.ts | 55 +++++--- apps/example/src/lib/store/index.ts | 11 +- apps/example/src/mocks/browser.ts | 18 ++- apps/example/src/mocks/db/index.ts | 14 +- apps/example/src/mocks/getHandlers.ts | 83 +++++++----- 23 files changed, 563 insertions(+), 467 deletions(-) diff --git a/apps/example/src/App.test.tsx b/apps/example/src/App.test.tsx index a99dff1a6..b6f0365bc 100644 --- a/apps/example/src/App.test.tsx +++ b/apps/example/src/App.test.tsx @@ -6,20 +6,19 @@ import React from "react" import { render } from "@testing-library/react" import App from "./App" -import { describe } from "node:test" +import { describe, it, expect, vi } from "vitest" import { screen } from "shadow-dom-testing-library" // Mock the styles vi.mock("./styles.module.scss", () => ({ - default: new Proxy(new Object(), { - // @ts-ignore - toString() { - return "/*TEST STYLES*/" - }, - }), + default: new Proxy( + {}, + { + get: () => "/*TEST STYLES*/", + } + ), })) -// eslint-disable-next-line @typescript-eslint/no-floating-promises describe("App", () => { it("should render the App component", () => { render() diff --git a/apps/example/src/App.tsx b/apps/example/src/App.tsx index ae6f138ce..2ed482069 100644 --- a/apps/example/src/App.tsx +++ b/apps/example/src/App.tsx @@ -1,17 +1,17 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ + /* * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect } from "react" -import PropTypes from "prop-types" +import React, { useEffect, useMemo } from "react" + // @ts-ignore import styles from "./styles.scss?inline" - import MonorepoChecker from "./components/MonorepoChecker" import { @@ -24,87 +24,75 @@ import { } from "@cloudoperators/juno-ui-components" import { mockedSession } from "@cloudoperators/juno-oauth" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { MessagesProvider } from "@cloudoperators/juno-messages-provider" + import AppContent from "./components/AppContent" import HeaderUser from "./components/auth/HeaderUser" import AsyncWorker from "./components/AsyncWorker" import StoreProvider, { useGlobalsActions, useAuthActions } from "./components/StoreProvider" -import { MessagesProvider } from "@cloudoperators/juno-messages-provider" -const App = (props = {}) => { +interface AppProps { + endpoint?: string + embedded?: boolean + id?: string + theme?: string +} + +const App: React.FC = ({ endpoint, embedded, id }) => { // @ts-ignore const { setEndpoint } = useGlobalsActions() // @ts-ignore const { setData } = useAuthActions() - // Create query client which it can be used from overall in the app const queryClient = new QueryClient() - // on app initial load save Endpoint and URL_STATE_KEY so it can be - // used from overall in the application useEffect(() => { - // set default endpoint so the useQueryClientFn can be used - // @ts-ignore - setEndpoint(props.endpoint) - }, []) + setEndpoint(endpoint || "") + }, [endpoint, setEndpoint]) - // fetch the mocked auth object and save it globally - const oidc = React.useMemo(() => { - // force fetch mocked session - return mockedSession({ - initialLogin: true, - onUpdate: (data: any) => { - setData(data) - }, - }) - }, []) + const oidc = useMemo( + () => + mockedSession({ + initialLogin: true, + onUpdate: setData, + }), + [setData] + ) - // @ts-ignore - console.debug("[exampleapp] embedded mode:", props.embedded) + console.debug("[exampleapp] embedded mode:", embedded) return ( - - {/* @ts-ignore */} - + {/* @ts-ignore */} + - {/* @ts-ignore */} } topNavigation={ - //@ts-ignore - {/* @ts-ignore */} - {/* @ts-ignore */} } > - {/* @ts-ignore */} - + ) } -App.propTypes = { - endpoint: PropTypes.string, - embedded: PropTypes.bool, - id: PropTypes.string, -} - -const StyledApp = (props: any) => { +const StyledApp: React.FC = (props) => { return ( - + // @ts-ignore + {/* load styles inside the shadow dom */} @@ -116,10 +104,4 @@ const StyledApp = (props: any) => { ) } -StyledApp.propTypes = { - theme: PropTypes.string, - embedded: PropTypes.bool, - endpoint: PropTypes.string, - id: PropTypes.string, -} export default StyledApp diff --git a/apps/example/src/components/AppContent.tsx b/apps/example/src/components/AppContent.tsx index d6f0b326e..2afd2e086 100644 --- a/apps/example/src/components/AppContent.tsx +++ b/apps/example/src/components/AppContent.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors * SPDX-License-Identifier: Apache-2.0 @@ -21,30 +18,32 @@ import { } from "@cloudoperators/juno-ui-components" import { useGlobalsActions, useGlobalsTabIndex, useAuthLoggedIn, useAuthError } from "./StoreProvider" import { useActions, Messages } from "@cloudoperators/juno-messages-provider" + import ModalManager from "./ModalManager" import PanelManager from "./PanelManager" import Peaks from "./peaks/Peaks" import WelcomeView from "./WelcomeView" -const AppContent = () => { - // @ts-ignore +const AppContent: React.FC = () => { const { setTabIndex, setCurrentModal } = useGlobalsActions() const loggedIn = useAuthLoggedIn() const authError = useAuthError() const tabIndex = useGlobalsTabIndex() + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { addMessage } = useActions() // set an error message when oidc fails useEffect(() => { if (authError) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call addMessage({ variant: "error", text: JSON.stringify(authError), }) } - }, [authError]) + }, [authError, addMessage]) - const onTabSelected = (index: any) => { + const onTabSelected = (index: number) => { setTabIndex(index) } @@ -53,23 +52,17 @@ const AppContent = () => { {loggedIn && !authError ? ( <> - {/* @ts-ignore */} - {/* @ts-ignore */} - {/* @ts-ignore */} - {/* @ts-ignore */} Peaks - {/* @ts-ignore */} Tab Two - {/* @ts-ignore */} {/* Set the background graphic using tailwind background image syntax as below. The image must exist at the specified location in your app */} {/* @@ -82,28 +75,20 @@ const AppContent = () => { - {/* @ts-ignore */}

Test a panel pressing the Button

- {/* @ts-ignore */} -