@hitachivantara/uikit-cli 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/package.json +68 -0
- package/src/app-shell.js +106 -0
- package/src/baselines/app-shell/vite/_gitignore +30 -0
- package/src/baselines/app-shell/vite/_oxlintrc.json +5 -0
- package/src/baselines/app-shell/vite/_package.json +55 -0
- package/src/baselines/app-shell/vite/public/locales/en/example.json +8 -0
- package/src/baselines/app-shell/vite/src/lib/data/config.ts +15 -0
- package/src/baselines/app-shell/vite/src/lib/i18n.ts +44 -0
- package/src/baselines/app-shell/vite/src/pages/Example/index.tsx +25 -0
- package/src/baselines/app-shell/vite/src/providers/Provider.tsx +31 -0
- package/src/baselines/app-shell/vite/src/tests/mocks.ts +1 -0
- package/src/baselines/app-shell/vite/src/tests/providers.tsx +13 -0
- package/src/baselines/app-shell/vite/src/tests/setupTests.ts +24 -0
- package/src/baselines/app-shell/vite/src/types/theme.d.ts +8 -0
- package/src/baselines/app-shell/vite/src/types/vite-env.d.ts +1 -0
- package/src/baselines/app-shell/vite/tsconfig.json +10 -0
- package/src/baselines/app-shell/vite/tsconfig.node.json +9 -0
- package/src/baselines/app-shell/vite/uno.config.ts +6 -0
- package/src/baselines/app-shell/vite/vite.config.ts +45 -0
- package/src/baselines/vite/_gitignore +30 -0
- package/src/baselines/vite/_oxlintrc.json +5 -0
- package/src/baselines/vite/_package.json +53 -0
- package/src/baselines/vite/index.html +18 -0
- package/src/baselines/vite/public/favicon.ico +0 -0
- package/src/baselines/vite/public/locales/en/common.json +16 -0
- package/src/baselines/vite/public/locales/en/home.json +4 -0
- package/src/baselines/vite/public/logo192.png +0 -0
- package/src/baselines/vite/src/App.tsx +31 -0
- package/src/baselines/vite/src/assets/HitachiLogo.tsx +27 -0
- package/src/baselines/vite/src/components/common/Loading/Loading.test.tsx +18 -0
- package/src/baselines/vite/src/components/common/Loading/Loading.tsx +15 -0
- package/src/baselines/vite/src/components/common/Loading/index.ts +1 -0
- package/src/baselines/vite/src/context/NavigationContext.tsx +67 -0
- package/src/baselines/vite/src/lib/i18n.ts +29 -0
- package/src/baselines/vite/src/main.tsx +12 -0
- package/src/baselines/vite/src/pages/Home/index.tsx +13 -0
- package/src/baselines/vite/src/pages/NotFound/NotFound.tsx +39 -0
- package/src/baselines/vite/src/pages/NotFound/index.tsx +1 -0
- package/src/baselines/vite/src/pages/layout/navigation.tsx +82 -0
- package/src/baselines/vite/src/routes.tsx +14 -0
- package/src/baselines/vite/src/tests/mocks.ts +1 -0
- package/src/baselines/vite/src/tests/providers.tsx +13 -0
- package/src/baselines/vite/src/tests/setupTests.ts +24 -0
- package/src/baselines/vite/src/types/theme.d.ts +8 -0
- package/src/baselines/vite/src/vite-env.d.ts +1 -0
- package/src/baselines/vite/tsconfig.json +10 -0
- package/src/baselines/vite/tsconfig.node.json +9 -0
- package/src/baselines/vite/uno.config.ts +6 -0
- package/src/baselines/vite/vite.config.ts +31 -0
- package/src/contents.js +63 -0
- package/src/create.js +172 -0
- package/src/index.js +22 -0
- package/src/navigation.js +21 -0
- package/src/package.js +37 -0
- package/src/plop-templates/README.md.hbs +10 -0
- package/src/plop-templates/app-shell/app-shell.config.ts.hbs +54 -0
- package/src/plop-templates/app-shell/index.html.hbs +15 -0
- package/src/plopfile.js +61 -0
- package/src/templates/AssetInventory/CardView.tsx +167 -0
- package/src/templates/AssetInventory/ListView.tsx +56 -0
- package/src/templates/AssetInventory/data.tsx +255 -0
- package/src/templates/AssetInventory/index.tsx +198 -0
- package/src/templates/AssetInventory/usePaginationData.ts +158 -0
- package/src/templates/Canvas/Context.tsx +49 -0
- package/src/templates/Canvas/ListView.tsx +189 -0
- package/src/templates/Canvas/Node.tsx +203 -0
- package/src/templates/Canvas/Sidebar.tsx +51 -0
- package/src/templates/Canvas/StatusEdge.tsx +75 -0
- package/src/templates/Canvas/StickyNode.tsx +475 -0
- package/src/templates/Canvas/Table.tsx +202 -0
- package/src/templates/Canvas/TreeView.tsx +211 -0
- package/src/templates/Canvas/dependencies.json +7 -0
- package/src/templates/Canvas/index.tsx +363 -0
- package/src/templates/Canvas/styles.tsx +41 -0
- package/src/templates/Canvas/utils.tsx +70 -0
- package/src/templates/Dashboard/GridPanel.tsx +33 -0
- package/src/templates/Dashboard/Kpi.tsx +107 -0
- package/src/templates/Dashboard/Map.styles.ts +681 -0
- package/src/templates/Dashboard/Map.tsx +71 -0
- package/src/templates/Dashboard/data.ts +67 -0
- package/src/templates/Dashboard/dependencies.json +11 -0
- package/src/templates/Dashboard/index.tsx +173 -0
- package/src/templates/DetailsView/KPIs.tsx +70 -0
- package/src/templates/DetailsView/MetadataItem.tsx +35 -0
- package/src/templates/DetailsView/Properties.tsx +127 -0
- package/src/templates/DetailsView/Table.tsx +104 -0
- package/src/templates/DetailsView/data.ts +67 -0
- package/src/templates/DetailsView/index.tsx +102 -0
- package/src/templates/DetailsView/usePaginationData.ts +155 -0
- package/src/templates/DetailsView/utils.ts +51 -0
- package/src/templates/Form/index.tsx +107 -0
- package/src/templates/KanbanBoard/ColumnContainer.tsx +89 -0
- package/src/templates/KanbanBoard/TaskCard.tsx +130 -0
- package/src/templates/KanbanBoard/data.tsx +140 -0
- package/src/templates/KanbanBoard/dependencies.json +6 -0
- package/src/templates/KanbanBoard/index.tsx +179 -0
- package/src/templates/KanbanBoard/styles.tsx +76 -0
- package/src/templates/KanbanBoard/types.ts +21 -0
- package/src/templates/ListView/Indicator.tsx +42 -0
- package/src/templates/ListView/Kpi.tsx +120 -0
- package/src/templates/ListView/Table.tsx +55 -0
- package/src/templates/ListView/data.tsx +179 -0
- package/src/templates/ListView/dependencies.json +5 -0
- package/src/templates/ListView/index.tsx +245 -0
- package/src/templates/ListView/usePaginationData.ts +158 -0
- package/src/templates/Welcome/index.tsx +101 -0
- package/src/templates/package.json +30 -0
- package/src/utils.js +37 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<link rel="apple-touch-icon" href="/logo192.png" />
|
|
8
|
+
<title>UI Kit App</title>
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&family=Open+Sans:wght@400;600&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="hv-root"></div>
|
|
16
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Lumada App",
|
|
3
|
+
|
|
4
|
+
"loading": {
|
|
5
|
+
"label": "Loading Page",
|
|
6
|
+
"errorTitle": "Unable to load page",
|
|
7
|
+
"errorMessage": "If this problem persists, please contact administrator.",
|
|
8
|
+
"errorAction": "Here has link to reload page."
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
"notFound": {
|
|
12
|
+
"title": "404 Page not found",
|
|
13
|
+
"message": "The page you followed may be broken or item has been removed.",
|
|
14
|
+
"action": "Click here to go back."
|
|
15
|
+
}
|
|
16
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import "./lib/i18n";
|
|
2
|
+
import "virtual:uno.css";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
createBrowserRouter,
|
|
6
|
+
RouteObject,
|
|
7
|
+
RouterProvider,
|
|
8
|
+
} from "react-router-dom";
|
|
9
|
+
import { HvProvider } from "@hitachivantara/uikit-react-core";
|
|
10
|
+
|
|
11
|
+
import { appRoutes } from "./routes";
|
|
12
|
+
|
|
13
|
+
export const routes: RouteObject[] = [
|
|
14
|
+
{
|
|
15
|
+
lazy: () => import("./pages/layout/navigation"),
|
|
16
|
+
children: appRoutes,
|
|
17
|
+
},
|
|
18
|
+
{ path: "*", lazy: () => import("./pages/NotFound") },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export const router = createBrowserRouter(routes, {
|
|
22
|
+
basename: import.meta.env.BASE_URL,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export default function App() {
|
|
26
|
+
return (
|
|
27
|
+
<HvProvider rootElementId="hv-root">
|
|
28
|
+
<RouterProvider router={router} />
|
|
29
|
+
</HvProvider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const HitachiLogo = ({ ...others }: React.SVGProps<SVGSVGElement>) => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
role="img"
|
|
5
|
+
viewBox="0 0 80 16"
|
|
6
|
+
width={72}
|
|
7
|
+
fill="currentColor"
|
|
8
|
+
{...others}
|
|
9
|
+
>
|
|
10
|
+
<title>Logo</title>
|
|
11
|
+
<path d="M63.6,2.5c0,0,0,4.8,0,4.7H70c0,0,0-4.7,0-4.7s3.1,0,3.1,0c0,0,0,11.8,0,11.9H70c0,0,0-5.2,0-5.2c0,0-6.3,0-6.3,0c0,0,0,5.2,0,5.2h-3.1c0,0,0-11.9,0-11.9C60.5,2.5,63.6,2.5,63.6,2.5z" />
|
|
12
|
+
<path d="M33.9,2.5c0,0,0,2,0,2h-4.8v9.8h-3.1V4.5h-4.8v-2C21.1,2.5,33.9,2.5,33.9,2.5z" />
|
|
13
|
+
<path d="M46,14.3c0,0-3.5,0-3.5,0l-1-2.6h-5.9c0,0-1,2.6-1,2.6h-3.5l5.5-11.9h3.8L46,14.3z M38.6,4.5l-2.2,5.4h4.4L38.6,4.5" />
|
|
14
|
+
<rect x="75.6" y="2.5" width="3.1" height="11.9" />
|
|
15
|
+
<path d="M4.9,2.5c0,0,0,4.8,0,4.7h6.3c0,0,0-4.7,0-4.7s3.1,0,3.1,0c0,0,0,11.8,0,11.9h-3.1c0,0,0-5.2,0-5.2c0,0-6.3,0-6.3,0c0,0,0,5.2,0,5.2H1.7c0,0,0-11.9,0-11.9C1.7,2.5,4.9,2.5,4.9,2.5z" />
|
|
16
|
+
<rect x="16.9" y="2.5" width="3.1" height="11.9" />
|
|
17
|
+
<path
|
|
18
|
+
d="M45.9,11c-0.3-0.8-0.4-1.6-0.4-2.5c0-1.2,0.2-2.4,0.8-3.4c0.6-1,1.5-1.8,2.7-2.2
|
|
19
|
+
c1.1-0.4,2.2-0.6,3.5-0.6c1.4,0,2.7,0.3,4,0.8c1.1,0.5,2,1.5,2.2,2.7c0.1,0.3,0.1,0.5,0.1,0.8h-3.3c0-0.3-0.1-0.6-0.2-0.9
|
|
20
|
+
c-0.3-0.6-0.8-1.2-1.5-1.4c-0.4-0.1-0.9-0.2-1.4-0.2c-0.5,0-1.1,0.1-1.5,0.3c-0.8,0.3-1.4,0.9-1.7,1.7
|
|
21
|
+
c-0.3,0.8-0.4,1.6-0.4,2.5c0,0.7,0.1,1.4,0.3,2.1c0.2,0.9,0.9,1.6,1.7,1.9c0.5,0.2,1.1,0.3,1.7,0.3c0.5,0,1-0.1,1.5-0.2
|
|
22
|
+
c0.6-0.2,1.1-0.6,1.4-1.2c0.2-0.4,0.3-0.8,0.3-1.3h3.3c0,0.4-0.1,0.8-0.2,1.1c-0.3,1.2-1.1,2.2-2.2,2.6
|
|
23
|
+
c-1.2,0.5-2.6,0.8-4,0.8c-1.1,0-2.2-0.2-3.2-0.5C47.8,13.6,46.5,12.5,45.9,11z"
|
|
24
|
+
/>
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { Loading } from "./Loading";
|
|
4
|
+
|
|
5
|
+
describe("<Loading />", () => {
|
|
6
|
+
it("renders the label", () => {
|
|
7
|
+
const MOCK_LABEL = "MOCK_LABEL";
|
|
8
|
+
render(<Loading label={MOCK_LABEL} />);
|
|
9
|
+
|
|
10
|
+
expect(screen.getByText(MOCK_LABEL)).toBeInTheDocument();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("renders the aria loading", () => {
|
|
14
|
+
render(<Loading />);
|
|
15
|
+
|
|
16
|
+
expect(screen.getByRole("progressbar")).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HvLoading } from "@hitachivantara/uikit-react-core";
|
|
2
|
+
|
|
3
|
+
export interface LoadingProps {
|
|
4
|
+
label?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** _Fullscreen_ `HvLoading` component to use with `Suspense` and other loading fallbacks */
|
|
8
|
+
export const Loading: React.FC<LoadingProps> = ({ label }) => (
|
|
9
|
+
<HvLoading
|
|
10
|
+
role="progressbar"
|
|
11
|
+
label={label}
|
|
12
|
+
aria-valuetext={label}
|
|
13
|
+
className="size-full z-overlay"
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Loading";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo } from "react";
|
|
2
|
+
import { useLocation } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
export interface NavigationData {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
data?: NavigationData[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface NavigationProviderProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
navigation: NavigationData[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface NavigationContextValue {
|
|
17
|
+
navigation: NavigationData[];
|
|
18
|
+
activePath: NavigationData | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const NavigationContext = createContext<NavigationContextValue>({
|
|
22
|
+
navigation: [],
|
|
23
|
+
activePath: undefined,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const getActivePath = (pathname: string, navigation: NavigationData[]) => {
|
|
27
|
+
return navigation.reduce((acc, item) => {
|
|
28
|
+
if (item.path && pathname.includes(item.path)) return item;
|
|
29
|
+
if (item.data) {
|
|
30
|
+
const found = item.data.find((child) => child.path === pathname);
|
|
31
|
+
if (found) return found;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return acc;
|
|
35
|
+
}, navigation[0]);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const NavigationProvider = ({
|
|
39
|
+
children,
|
|
40
|
+
navigation,
|
|
41
|
+
}: NavigationProviderProps) => {
|
|
42
|
+
const { pathname } = useLocation();
|
|
43
|
+
|
|
44
|
+
const value = useMemo(
|
|
45
|
+
() => ({
|
|
46
|
+
navigation,
|
|
47
|
+
activePath: getActivePath(pathname, navigation),
|
|
48
|
+
}),
|
|
49
|
+
[navigation, pathname],
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<NavigationContext.Provider value={value}>
|
|
54
|
+
{children}
|
|
55
|
+
</NavigationContext.Provider>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function useNavigationContext() {
|
|
60
|
+
const context = useContext(NavigationContext);
|
|
61
|
+
if (!context) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"useNavigationContext must be used within a NavigationProvider",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return context;
|
|
67
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { initReactI18next } from "react-i18next";
|
|
2
|
+
import { createInstance, type i18n } from "i18next";
|
|
3
|
+
import LanguageDetector from "i18next-browser-languagedetector";
|
|
4
|
+
import Backend, { type HttpBackendOptions } from "i18next-http-backend";
|
|
5
|
+
|
|
6
|
+
export const i18nInstance: i18n = createInstance();
|
|
7
|
+
|
|
8
|
+
i18nInstance
|
|
9
|
+
// load translation using xhr -> see /public/locales
|
|
10
|
+
// learn more: https://github.com/i18next/i18next-xhr-backend
|
|
11
|
+
.use(Backend)
|
|
12
|
+
// detect user language
|
|
13
|
+
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
|
14
|
+
.use(LanguageDetector)
|
|
15
|
+
// pass the i18n instance to react-i18next.
|
|
16
|
+
.use(initReactI18next)
|
|
17
|
+
// init i18next
|
|
18
|
+
// for all options read: https://www.i18next.com/overview/configuration-options
|
|
19
|
+
.init<HttpBackendOptions>({
|
|
20
|
+
fallbackLng: "en",
|
|
21
|
+
supportedLngs: ["en"],
|
|
22
|
+
backend: {
|
|
23
|
+
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
|
24
|
+
},
|
|
25
|
+
interpolation: {
|
|
26
|
+
escapeValue: false, // not needed for react as it escapes by default
|
|
27
|
+
},
|
|
28
|
+
load: "languageOnly",
|
|
29
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useTranslation } from "react-i18next";
|
|
2
|
+
import { HvTypography } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
|
|
4
|
+
export function Component() {
|
|
5
|
+
const { t } = useTranslation("home");
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="grid gap-sm">
|
|
9
|
+
<HvTypography variant="title1">{t("title")}</HvTypography>
|
|
10
|
+
<HvTypography>{t("description")}</HvTypography>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useTranslation } from "react-i18next";
|
|
2
|
+
import { useNavigate } from "react-router-dom";
|
|
3
|
+
import { css } from "@emotion/css";
|
|
4
|
+
import {
|
|
5
|
+
HvButton,
|
|
6
|
+
HvEmptyState,
|
|
7
|
+
theme,
|
|
8
|
+
} from "@hitachivantara/uikit-react-core";
|
|
9
|
+
import { Info } from "@hitachivantara/uikit-react-icons";
|
|
10
|
+
|
|
11
|
+
const classes = {
|
|
12
|
+
root: css({
|
|
13
|
+
height: `calc(100vh - ${theme.header.height})`,
|
|
14
|
+
}),
|
|
15
|
+
empty: css({
|
|
16
|
+
alignItems: "center",
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const Component = () => {
|
|
21
|
+
const { t } = useTranslation("common");
|
|
22
|
+
const navigate = useNavigate();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className={classes.root}>
|
|
26
|
+
<HvEmptyState
|
|
27
|
+
className={classes.empty}
|
|
28
|
+
title={t("notFound.title")}
|
|
29
|
+
message={t("notFound.message")}
|
|
30
|
+
icon={<Info />}
|
|
31
|
+
action={
|
|
32
|
+
<HvButton variant="primaryGhost" onClick={() => navigate(-1)}>
|
|
33
|
+
{t("notFound.action")}
|
|
34
|
+
</HvButton>
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./NotFound";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
2
|
+
import { Outlet, useNavigate } from "react-router-dom";
|
|
3
|
+
import { useTheme } from "@mui/material/styles";
|
|
4
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
5
|
+
import {
|
|
6
|
+
HvButton,
|
|
7
|
+
HvContainer,
|
|
8
|
+
HvContainerProps,
|
|
9
|
+
HvHeader,
|
|
10
|
+
HvHeaderBrand,
|
|
11
|
+
HvHeaderNavigation,
|
|
12
|
+
theme,
|
|
13
|
+
} from "@hitachivantara/uikit-react-core";
|
|
14
|
+
import { Menu } from "@hitachivantara/uikit-react-icons";
|
|
15
|
+
|
|
16
|
+
import { HitachiLogo } from "../../assets/HitachiLogo";
|
|
17
|
+
import { Loading } from "../../components/common/Loading";
|
|
18
|
+
import {
|
|
19
|
+
NavigationProvider,
|
|
20
|
+
useNavigationContext,
|
|
21
|
+
} from "../../context/NavigationContext";
|
|
22
|
+
import { navigationData } from "../../routes";
|
|
23
|
+
|
|
24
|
+
export function Component() {
|
|
25
|
+
return (
|
|
26
|
+
<NavigationProvider navigation={navigationData}>
|
|
27
|
+
<Header />
|
|
28
|
+
<Container maxWidth="xl">
|
|
29
|
+
<Outlet />
|
|
30
|
+
</Container>
|
|
31
|
+
</NavigationProvider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Header() {
|
|
36
|
+
const navigate = useNavigate();
|
|
37
|
+
const theme = useTheme();
|
|
38
|
+
const { activePath, navigation } = useNavigationContext();
|
|
39
|
+
|
|
40
|
+
const isMdUp = useMediaQuery(theme.breakpoints.up("md"));
|
|
41
|
+
const isXs = useMediaQuery(theme.breakpoints.only("xs"));
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<HvHeader>
|
|
45
|
+
{!isMdUp && (
|
|
46
|
+
<div>
|
|
47
|
+
<HvButton variant="primaryGhost" icon>
|
|
48
|
+
<Menu />
|
|
49
|
+
</HvButton>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
<HvHeaderBrand
|
|
54
|
+
logo={<HitachiLogo />}
|
|
55
|
+
name={!isXs ? "Lumada App" : undefined}
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
{isMdUp && (
|
|
59
|
+
<HvHeaderNavigation
|
|
60
|
+
data={navigation}
|
|
61
|
+
selected={activePath?.id}
|
|
62
|
+
onClick={(event, selection) => {
|
|
63
|
+
if (selection.path) navigate(selection.path);
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
)}
|
|
67
|
+
</HvHeader>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Container({ maxWidth = "lg", children }: HvContainerProps) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
className="flex pb-lg min-h-screen"
|
|
75
|
+
style={{ paddingTop: `calc(${theme.header.height} + ${theme.space.lg})` }}
|
|
76
|
+
>
|
|
77
|
+
<HvContainer maxWidth={maxWidth} component="main">
|
|
78
|
+
<Suspense fallback={<Loading />}>{children}</Suspense>
|
|
79
|
+
</HvContainer>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RouteObject } from "react-router-dom";
|
|
2
|
+
import type { NavigationData } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
|
|
4
|
+
export const appRoutes: RouteObject[] = [
|
|
5
|
+
{ index: true, path: "/", lazy: () => import("./pages/Home") },
|
|
6
|
+
// APP ROUTES
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
// Automatic `navigationData`. Change to manual & i18n labels if needed.
|
|
10
|
+
export const navigationData = appRoutes.map<NavigationData>((route) => ({
|
|
11
|
+
id: route.path ?? "",
|
|
12
|
+
label: (route.path ?? "").split("-").join(" "),
|
|
13
|
+
path: route.path,
|
|
14
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SUSPENSE_LABEL = "LOADING";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
2
|
+
import { HvProvider } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
|
|
4
|
+
import { SUSPENSE_LABEL } from "./mocks";
|
|
5
|
+
|
|
6
|
+
/** Base Test Provider */
|
|
7
|
+
export const TestProvider = ({ children }: { children: React.ReactNode }) => {
|
|
8
|
+
return (
|
|
9
|
+
<Suspense fallback={SUSPENSE_LABEL}>
|
|
10
|
+
<HvProvider>{children}</HvProvider>
|
|
11
|
+
</Suspense>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TransProps } from "react-i18next";
|
|
2
|
+
import type { ThirdPartyModule, TOptionsBase } from "i18next";
|
|
3
|
+
import { vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
import "@testing-library/jest-dom";
|
|
6
|
+
|
|
7
|
+
vi.mock("react-i18next", async () => {
|
|
8
|
+
const { initReactI18next } = await vi.importActual<{
|
|
9
|
+
initReactI18next: ThirdPartyModule;
|
|
10
|
+
}>("react-i18next");
|
|
11
|
+
|
|
12
|
+
const t = (str: string, options?: TOptionsBase) =>
|
|
13
|
+
options?.returnObjects ? undefined : str;
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
initReactI18next,
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
Trans: ({ i18nKey }: TransProps<any>) => i18nKey,
|
|
19
|
+
useTranslation: () => ({
|
|
20
|
+
t,
|
|
21
|
+
i18n: { changeLanguage: async () => t },
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Theme } from "@mui/material/styles";
|
|
2
|
+
|
|
3
|
+
// makeStyles is now exported from @mui/styles package which does not know about Theme
|
|
4
|
+
// we need to augment the DefaultTheme (empty object) in @mui/styles with Theme from the core.
|
|
5
|
+
declare module "@mui/private-theming" {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
7
|
+
interface DefaultTheme extends Theme {}
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
/// <reference types="vitest" />
|
|
3
|
+
|
|
4
|
+
import react from "@vitejs/plugin-react";
|
|
5
|
+
import unoCSS from "unocss/vite";
|
|
6
|
+
import { defineConfig } from "vite";
|
|
7
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
plugins: [react(), tsconfigPaths(), unoCSS()],
|
|
11
|
+
test: {
|
|
12
|
+
globals: true,
|
|
13
|
+
environment: "happy-dom",
|
|
14
|
+
setupFiles: ["src/tests/setupTests.ts"],
|
|
15
|
+
reporters: "default",
|
|
16
|
+
coverage: {
|
|
17
|
+
enabled: false, // disabled by default. run vitest with --coverage
|
|
18
|
+
provider: "v8",
|
|
19
|
+
reporter: "lcov",
|
|
20
|
+
include: ["src/**/*.ts?(x)"],
|
|
21
|
+
exclude: [
|
|
22
|
+
"src/**/mocks/*",
|
|
23
|
+
"src/**/tests/*",
|
|
24
|
+
"src/**/*.test.ts?(x)",
|
|
25
|
+
"src/**/styles.[jt]s?(x)",
|
|
26
|
+
"src/**/*.d.ts",
|
|
27
|
+
"src/*.tsx",
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
package/src/contents.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import nodePlop from "node-plop";
|
|
4
|
+
|
|
5
|
+
import { __dirname, mergeDependenciesObject } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
const plop = await nodePlop(`${__dirname}/plopfile.js`);
|
|
8
|
+
|
|
9
|
+
const createReadMe = plop.getGenerator("createReadMe");
|
|
10
|
+
|
|
11
|
+
const getTemplateDependencies = (templatePath) => {
|
|
12
|
+
// read and remove template dependencies.json file if exists
|
|
13
|
+
const dependenciesPath = `${templatePath}/dependencies.json`;
|
|
14
|
+
const dependenciesExist = fs.existsSync(dependenciesPath);
|
|
15
|
+
const dependencies = dependenciesExist
|
|
16
|
+
? fs.readJsonSync(dependenciesPath)
|
|
17
|
+
: {};
|
|
18
|
+
|
|
19
|
+
fs.removeSync(dependenciesPath);
|
|
20
|
+
|
|
21
|
+
return dependencies;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const copyTemplateContents = (appPath, templates) => {
|
|
25
|
+
const destPath = `${appPath}/src`;
|
|
26
|
+
|
|
27
|
+
// object for collecting extra dependencies coming from selected templates or other options
|
|
28
|
+
const dependencies = {
|
|
29
|
+
dependencies: {},
|
|
30
|
+
devDependencies: {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (const template of templates) {
|
|
34
|
+
console.log(`Creating template: ${chalk.cyan(template)}`);
|
|
35
|
+
|
|
36
|
+
const templateSrc = `${__dirname}/templates/${template}`;
|
|
37
|
+
const templateDest = `${destPath}/pages/${template}`;
|
|
38
|
+
|
|
39
|
+
fs.copySync(templateSrc, templateDest, { overwrite: true });
|
|
40
|
+
|
|
41
|
+
mergeDependenciesObject(
|
|
42
|
+
getTemplateDependencies(templateDest),
|
|
43
|
+
dependencies,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return dependencies;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const createReadMeFile = async (path, name) => {
|
|
51
|
+
await createReadMe.runActions({
|
|
52
|
+
path,
|
|
53
|
+
appName: name.replace(/([a-z])([A-Z])/g, "$1 $2"),
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const createAppContents = async (appPath, name, templates) => {
|
|
58
|
+
console.log(`Creating ${chalk.cyan(name)} contents\n`);
|
|
59
|
+
|
|
60
|
+
await createReadMeFile(appPath, name);
|
|
61
|
+
|
|
62
|
+
return templates?.length ? copyTemplateContents(appPath, templates) : {};
|
|
63
|
+
};
|