Skip to content

Commit

Permalink
fix: ui use react routers base path and add tests for base path calcu…
Browse files Browse the repository at this point in the history
…lation

Signed-off-by: David Edler <[email protected]>
  • Loading branch information
edlerd committed Jun 21, 2024
1 parent 709399c commit 85da4c0
Show file tree
Hide file tree
Showing 17 changed files with 509 additions and 64 deletions.
6 changes: 4 additions & 2 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"hooks-add": "husky install",
"hooks-remove": "husky uninstall",
"start": "concurrently --kill-others --raw 'yarn dev-serve' 'yarn proxy-serve'",
"test-js": "vitest --run",
"dev-serve": "vite --host | grep -v localhost",
"proxy-serve": "./entrypoint"
},
Expand Down Expand Up @@ -67,8 +68,9 @@
"stylelint-prettier": "5.0.0",
"stylelint-scss": "6.2.1",
"typescript": "5.4.3",
"vite": "^5.2.2",
"vite-tsconfig-paths": "4.3.2"
"vite": "5.2.8",
"vite-tsconfig-paths": "4.3.2",
"vitest": "1.2.1"
},
"lint-staged": {
"src/**/*.{json,jsx,ts,tsx}": [
Expand Down
18 changes: 9 additions & 9 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SchemaList from "pages/schemas/SchemaList";
import Navigation from "components/Navigation";
import Panels from "components/Panels";
import useLocalStorage from "util/useLocalStorage";
import { apiBasePath, basePath } from "util/basePaths";
import { apiBasePath } from "util/basePaths";

const App: FC = () => {
// Store a user token that will be passed to the API using the
Expand All @@ -34,21 +34,21 @@ const App: FC = () => {
<Suspense fallback={<Loader />}>
<Routes>
<Route
path={basePath}
path="/"
element={
<Login isAuthenticated={!!authUser} setAuthUser={setAuthUser} />
}
>
<Route
path={basePath}
element={<Navigate to={`${basePath}provider`} replace={true} />}
path="/"
element={<Navigate to="/provider" replace={true} />}
/>
<Route path={`${basePath}provider`} element={<ProviderList />} />
<Route path={`${basePath}client`} element={<ClientList />} />
<Route path={`${basePath}identity`} element={<IdentityList />} />
<Route path={`${basePath}schema`} element={<SchemaList />} />
<Route path="/provider" element={<ProviderList />} />
<Route path="/client" element={<ClientList />} />
<Route path="/identity" element={<IdentityList />} />
<Route path="/schema" element={<SchemaList />} />
<Route
path={basePath + "*"}
path="/*"
element={
<ReBACAdmin
apiURL={apiBasePath}
Expand Down
3 changes: 1 addition & 2 deletions ui/src/components/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { FC } from "react";
import { NavLink } from "react-router-dom";
import { basePath } from "util/basePaths";

const Logo: FC = () => {
return (
<NavLink className="p-panel__logo" to={basePath}>
<NavLink className="p-panel__logo" to="/">
<div className="p-navigation__logo-tag">
<img
className="p-navigation__logo-icon"
Expand Down
13 changes: 6 additions & 7 deletions ui/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Button, Icon } from "@canonical/react-components";
import classnames from "classnames";
import Logo from "components/Logo";
import { GroupsLink, RolesLink } from "@canonical/rebac-admin";
import { basePath } from "util/basePaths";

type Props = {
username?: string;
Expand Down Expand Up @@ -38,7 +37,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`${basePath}provider`}
to="/provider"
title={`Provider list`}
>
<Icon
Expand All @@ -51,7 +50,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`${basePath}client`}
to="/client"
title={`Client list`}
>
<Icon
Expand All @@ -64,7 +63,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`${basePath}identity`}
to="/identity"
title={`Identity list`}
>
<Icon
Expand All @@ -77,7 +76,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`${basePath}schema`}
to="/schema"
title={`Schema list`}
>
<Icon
Expand All @@ -90,15 +89,15 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<GroupsLink
className="p-side-navigation__link"
baseURL={basePath}
baseURL="/"
icon="user-group"
iconIsLight
/>
</li>
<li className="p-side-navigation__item secondary">
<RolesLink
className="p-side-navigation__link"
baseURL={basePath}
baseURL="/"
icon="profile"
iconIsLight
/>
Expand Down
3 changes: 2 additions & 1 deletion ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import App from "./App";
import "./sass/styles.scss";
import { NotificationProvider } from "@canonical/react-components";
import { basePath } from "util/basePaths";

const queryClient = new QueryClient();

const rootElement = document.getElementById("app");
if (!rootElement) throw new Error("Failed to find the root element");
const root = createRoot(rootElement);
root.render(
<Router>
<Router basename={basePath}>
<QueryClientProvider client={queryClient}>
<NotificationProvider>
<App />
Expand Down
8 changes: 2 additions & 6 deletions ui/src/pages/clients/ClientCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import ClientForm, { ClientFormTypes } from "pages/clients/ClientForm";
import { createClient } from "api/client";
import SidePanel from "components/SidePanel";
import ScrollableContainer from "components/ScrollableContainer";
import { basePath } from "util/basePaths";

const ClientCreate: FC = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -44,7 +43,7 @@ const ClientCreate: FC = () => {
queryKey: [queryKeys.clients],
});
const msg = `Client created. Id: ${result.client_id} Secret: ${result.client_secret}`;
navigate(`${basePath}client`, notify.queue(notify.success(msg)));
navigate("/client", notify.queue(notify.success(msg)));
})
.catch((e) => {
formik.setSubmitting(false);
Expand Down Expand Up @@ -73,10 +72,7 @@ const ClientCreate: FC = () => {
<SidePanel.Footer>
<Row className="u-align-text--right">
<Col size={12}>
<Button
appearance="base"
onClick={() => navigate(`${basePath}client`)}
>
<Button appearance="base" onClick={() => navigate("/client")}>
Cancel
</Button>
<ActionButton
Expand Down
8 changes: 2 additions & 6 deletions ui/src/pages/clients/ClientEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { fetchClient, updateClient } from "api/client";
import usePanelParams from "util/usePanelParams";
import SidePanel from "components/SidePanel";
import ScrollableContainer from "components/ScrollableContainer";
import { basePath } from "util/basePaths";

const ClientEdit: FC = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -56,10 +55,7 @@ const ClientEdit: FC = () => {
void queryClient.invalidateQueries({
queryKey: [queryKeys.clients],
});
navigate(
`${basePath}client`,
notify.queue(notify.success("Client updated")),
);
navigate("/client", notify.queue(notify.success("Client updated")));
})
.catch((e) => {
formik.setSubmitting(false);
Expand Down Expand Up @@ -91,7 +87,7 @@ const ClientEdit: FC = () => {
<Button
appearance="base"
className="u-no-margin--bottom u-sv2"
onClick={() => navigate(`${basePath}client`)}
onClick={() => navigate("/client")}
>
Cancel
</Button>
Expand Down
3 changes: 1 addition & 2 deletions ui/src/pages/clients/DeleteClientBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useQueryClient } from "@tanstack/react-query";
import { ConfirmationButton, useNotify } from "@canonical/react-components";
import { deleteClient } from "api/client";
import { Client } from "types/client";
import { basePath } from "util/basePaths";

interface Props {
client: Client;
Expand All @@ -22,7 +21,7 @@ const DeleteClientBtn: FC<Props> = ({ client }) => {
deleteClient(client.client_id)
.then(() => {
navigate(
`${basePath}client`,
"/client",
notify.queue(notify.success(`Client ${client.client_name} deleted.`)),
);
})
Expand Down
3 changes: 1 addition & 2 deletions ui/src/pages/identities/DeleteIdentityBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useQueryClient } from "@tanstack/react-query";
import { ConfirmationButton, useNotify } from "@canonical/react-components";
import { Identity } from "types/identity";
import { deleteIdentity } from "api/identities";
import { basePath } from "util/basePaths";

interface Props {
identity: Identity;
Expand All @@ -22,7 +21,7 @@ const DeleteIdentityBtn: FC<Props> = ({ identity }) => {
deleteIdentity(identity.id)
.then(() => {
navigate(
`${basePath}identity`,
"/identity",
notify.queue(
notify.success(`Identity ${identity.traits?.email} deleted.`),
),
Expand Down
8 changes: 2 additions & 6 deletions ui/src/pages/identities/IdentityCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import IdentityForm, { IdentityFormTypes } from "pages/identities/IdentityForm";
import { createIdentity } from "api/identities";
import SidePanel from "components/SidePanel";
import ScrollableContainer from "components/ScrollableContainer";
import { basePath } from "util/basePaths";

const IdentityCreate: FC = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -45,7 +44,7 @@ const IdentityCreate: FC = () => {
queryKey: [queryKeys.identities],
});
const msg = `Identity created.`;
navigate(`${basePath}identity`, notify.queue(notify.success(msg)));
navigate("/identity", notify.queue(notify.success(msg)));
})
.catch((e) => {
formik.setSubmitting(false);
Expand Down Expand Up @@ -74,10 +73,7 @@ const IdentityCreate: FC = () => {
<SidePanel.Footer>
<Row className="u-align-text--right">
<Col size={12}>
<Button
appearance="base"
onClick={() => navigate(`${basePath}identity`)}
>
<Button appearance="base" onClick={() => navigate("/identity")}>
Cancel
</Button>
<ActionButton
Expand Down
3 changes: 1 addition & 2 deletions ui/src/pages/providers/DeleteProviderBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import { deleteProvider } from "api/provider";
import { IdentityProvider } from "types/provider";
import usePortal from "react-useportal";
import { basePath } from "util/basePaths";

interface Props {
provider: IdentityProvider;
Expand All @@ -32,7 +31,7 @@ const DeleteProviderBtn: FC<Props> = ({ provider }) => {
deleteProvider(provider.id)
.then(() => {
navigate(
`${basePath}provider`,
"/provider",
notify.queue(notify.success(`Provider ${provider.id} deleted.`)),
);
})
Expand Down
8 changes: 2 additions & 6 deletions ui/src/pages/providers/ProviderCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import ProviderForm, { ProviderFormTypes } from "pages/providers/ProviderForm";
import { createProvider } from "api/provider";
import SidePanel from "components/SidePanel";
import ScrollableContainer from "components/ScrollableContainer";
import { basePath } from "util/basePaths";

const ProviderCreate: FC = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -46,7 +45,7 @@ const ProviderCreate: FC = () => {
queryKey: [queryKeys.providers],
});
const msg = `Provider created.`;
navigate(`${basePath}provider`, notify.queue(notify.success(msg)));
navigate("/provider", notify.queue(notify.success(msg)));
})
.catch((e) => {
formik.setSubmitting(false);
Expand Down Expand Up @@ -75,10 +74,7 @@ const ProviderCreate: FC = () => {
<SidePanel.Footer>
<Row className="u-align-text--right">
<Col size={12}>
<Button
appearance="base"
onClick={() => navigate(`${basePath}provider`)}
>
<Button appearance="base" onClick={() => navigate("/provider")}>
Cancel
</Button>
<ActionButton
Expand Down
33 changes: 33 additions & 0 deletions ui/src/util/basePaths.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { calculateBasePath } from "./basePaths";

describe("calculateBasePath", () => {
it("resolves with ui path", () => {
vi.stubGlobal("location", { pathname: "/ui/" });
const result = calculateBasePath();
expect(result).toBe("/ui/");
});

it("resolves with ui path and discards detail page location", () => {
vi.stubGlobal("location", { pathname: "/ui/foo/bar" });
const result = calculateBasePath();
expect(result).toBe("/ui/");
});

it("resolves with prefixed ui path", () => {
vi.stubGlobal("location", { pathname: "/prefix/ui/" });
const result = calculateBasePath();
expect(result).toBe("/prefix/ui/");
});

it("resolves with prefixed ui path on a detail page", () => {
vi.stubGlobal("location", { pathname: "/prefix/ui/foo/bar/baz" });
const result = calculateBasePath();
expect(result).toBe("/prefix/ui/");
});

it("resolves with root path if /ui/ is not part of the pathname", () => {
vi.stubGlobal("location", { pathname: "/foo/bar/baz" });
const result = calculateBasePath();
expect(result).toBe("/");
});
});
2 changes: 1 addition & 1 deletion ui/src/util/basePaths.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
type BasePath = `/${string}`;

const calculateBasePath = (): BasePath => {
export const calculateBasePath = (): BasePath => {
const path = window.location.pathname;
// find first occurrence of /ui/ and return the string before it
const basePath = path.match(/(.*\/ui\/)/);
Expand Down
5 changes: 3 additions & 2 deletions ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
"isolatedModules": true,
"noEmit": false,
"sourceMap": true,
"types": ["vite/client"]
"types": ["vite/client", "vitest/globals"]
},
"include": [
"./src",
".eslintrc.js",
"babel.config.js",
".eslintrc.cjs",
"vite.config.ts"
"vite.config.ts",
"vitest.config.ts"
]
}
13 changes: 13 additions & 0 deletions ui/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { mergeConfig, defineConfig } from "vitest/config";
import viteConfig from "./vite.config";

export default mergeConfig(
viteConfig({ mode: "development" }),
defineConfig({
test: {
environment: "jsdom",
globals: true,
include: ["./src/**/*.spec.{ts,tsx}"],
},
}),
);
Loading

0 comments on commit 85da4c0

Please sign in to comment.