Skip to content

Commit

Permalink
feat: move SSL key delete form to side panel (#5341)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jay-Topher committed Mar 12, 2024
1 parent b674e98 commit 44d7ffb
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 177 deletions.
12 changes: 12 additions & 0 deletions src/app/preferences/components/Routes/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Redirect } from "react-router";
import { Route, Routes as ReactRouterRoutes } from "react-router-dom-v5-compat";

import DeleteSSHKey from "../../views/SSHKeys/DeleteSSHKey";
import DeleteSSLKey from "../../views/SSLKeys/DeleteSSLKey";

import PageContent from "@/app/base/components/PageContent";
import urls from "@/app/base/urls";
Expand Down Expand Up @@ -125,6 +126,17 @@ const Routes = (): JSX.Element => {
}
path={getRelativeRoute(urls.preferences.sslKeys.add, base)}
/>
<Route
element={
<PageContent
sidePanelContent={<DeleteSSLKey />}
sidePanelTitle="Delete SSL key"
>
<SSLKeyList />
</PageContent>
}
path={getRelativeRoute(urls.preferences.sslKeys.delete(null), base)}
/>
<Route element={<NotFound />} path="*" />
</ReactRouterRoutes>
);
Expand Down
5 changes: 5 additions & 0 deletions src/app/preferences/urls.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { SSLKey, SSLKeyMeta } from "../store/sslkey/types";

import type { Token } from "@/app/store/token/types";
import { argPath } from "@/app/utils";

Expand All @@ -17,6 +19,9 @@ const urls = {
},
sslKeys: {
add: "/account/prefs/ssl-keys/add",
delete: argPath<{ id: SSLKey[SSLKeyMeta.PK] }>(
"/account/prefs/ssl-keys/:id/delete"
),
index: "/account/prefs/ssl-keys",
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import configureStore from "redux-mock-store";

import DeleteSSLKey from "./DeleteSSLKey";

import { Label as SSLKeyListLabels } from "@/app/preferences/views/SSLKeys/SSLKeyList/SSLKeyList";
import type { RootState } from "@/app/store/root/types";
import {
sslKey as sslKeyFactory,
sslKeyState as sslKeyStateFactory,
rootState as rootStateFactory,
} from "@/testing/factories";
import { screen, renderWithBrowserRouter, userEvent } from "@/testing/utils";

const mockStore = configureStore<RootState>();

let state: RootState;

beforeEach(() => {
state = rootStateFactory({
sslkey: sslKeyStateFactory({
loading: false,
loaded: true,
items: [
sslKeyFactory({
id: 1,
key: "ssh-rsa aabb",
}),
sslKeyFactory({
id: 2,
key: "ssh-rsa ccdd",
}),
sslKeyFactory({
id: 3,
key: "ssh-rsa eeff",
}),
sslKeyFactory({
id: 4,
key: "ssh-rsa gghh",
}),
sslKeyFactory({ id: 5, key: "ssh-rsa gghh" }),
],
}),
});
});

it("can show a delete confirmation", () => {
renderWithBrowserRouter(<DeleteSSLKey />, {
route: "/account/prefs/ssl-keys/1/delete",
routePattern: "/account/prefs/ssl-keys/:id/delete",
state,
});
expect(screen.getByRole("form", { name: SSLKeyListLabels.DeleteConfirm }));
expect(
screen.getByText(/Are you sure you want to delete this SSL key?/i)
).toBeInTheDocument();
});

it("can delete an SSL key", async () => {
const store = mockStore(state);
renderWithBrowserRouter(<DeleteSSLKey />, {
route: "/account/prefs/ssl-keys/1/delete",
routePattern: "/account/prefs/ssl-keys/:id/delete",
store,
});

await userEvent.click(screen.getByRole("button", { name: "Delete" }));
expect(
store.getActions().find((action) => action.type === "sslkey/delete")
).toEqual({
type: "sslkey/delete",
payload: {
params: {
id: 1,
},
},
meta: {
model: "sslkey",
method: "delete",
},
});
});
45 changes: 45 additions & 0 deletions src/app/preferences/views/SSLKeys/DeleteSSLKey/DeleteSSLKey.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useOnEscapePressed } from "@canonical/react-components";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom-v5-compat";

import ModelActionForm from "@/app/base/components/ModelActionForm";
import { useAddMessage, useGetURLId } from "@/app/base/hooks";
import urls from "@/app/preferences/urls";
import { Label } from "@/app/preferences/views/SSLKeys/SSLKeyList/SSLKeyList";
import { actions as sslkeyActions } from "@/app/store/sslkey";
import sslkeySelectors from "@/app/store/sslkey/selectors";
import { isId } from "@/app/utils";

const DeleteSSLKey = () => {
const id = useGetURLId("id");
const navigate = useNavigate();
const dispatch = useDispatch();
const saved = useSelector(sslkeySelectors.saved);
const saving = useSelector(sslkeySelectors.saving);
const onClose = () => navigate({ pathname: urls.sslKeys.index });
useOnEscapePressed(() => onClose());
useAddMessage(saved, sslkeyActions.cleanup, "SSL key removed successfully.");

if (!isId(id)) {
return <h4>SSL key not found</h4>;
}

return (
<ModelActionForm
aria-label={Label.DeleteConfirm}
initialValues={{}}
modelType="SSL key"
onCancel={onClose}
onSubmit={() => {
dispatch(sslkeyActions.delete(id));
}}
saved={saved}
savedRedirect={urls.sslKeys.index}
saving={saving}
submitAppearance="negative"
submitLabel="Delete"
/>
);
};

export default DeleteSSLKey;
1 change: 1 addition & 0 deletions src/app/preferences/views/SSLKeys/DeleteSSLKey/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./DeleteSSLKey";
109 changes: 0 additions & 109 deletions src/app/preferences/views/SSLKeys/SSLKeyList/SSLKeyList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Provider } from "react-redux";
import { MemoryRouter } from "react-router-dom";
import { CompatRouter } from "react-router-dom-v5-compat";
import configureStore from "redux-mock-store";

import SSLKeyList, { Label as SSLKeyListLabels } from "./SSLKeyList";

Expand All @@ -12,16 +10,11 @@ import {
rootState as rootStateFactory,
} from "@/testing/factories";
import {
userEvent,
screen,
render,
within,
renderWithMockStore,
renderWithBrowserRouter,
} from "@/testing/utils";

const mockStore = configureStore();

describe("SSLKeyList", () => {
let state: RootState;

Expand Down Expand Up @@ -103,108 +96,6 @@ describe("SSLKeyList", () => {
expect(screen.getByRole("grid", { name: SSLKeyListLabels.Title }));
});

it("can show a delete confirmation", async () => {
renderWithMockStore(
<MemoryRouter
initialEntries={[
{ pathname: "/account/prefs/ssl-keys", key: "testKey" },
]}
>
<CompatRouter>
<SSLKeyList />
</CompatRouter>
</MemoryRouter>,
{ state }
);
let row = screen.getByRole("row", { name: "ssh-rsa aabb" });
expect(row).not.toHaveClass("is-active");
// Click on the delete button:
await userEvent.click(within(row).getByRole("button", { name: "Delete" }));
row = screen.getByRole("row", { name: "ssh-rsa aabb" });
expect(row).toHaveClass("is-active");
});

it("can delete a SSL key", async () => {
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter
initialEntries={[
{ pathname: "/account/prefs/ssl-keys", key: "testKey" },
]}
>
<CompatRouter>
<SSLKeyList />
</CompatRouter>
</MemoryRouter>
</Provider>
);
let row = screen.getByRole("row", { name: "ssh-rsa aabb" });

// Click on the delete button:
await userEvent.click(within(row).getByRole("button", { name: "Delete" }));

// Click on the delete confirm button
await userEvent.click(
within(
within(row).getByLabelText(SSLKeyListLabels.DeleteConfirm)
).getByRole("button", {
name: "Delete",
})
);
expect(
store.getActions().find((action) => action.type === "sslkey/delete")
).toEqual({
type: "sslkey/delete",
payload: {
params: {
id: 1,
},
},
meta: {
model: "sslkey",
method: "delete",
},
});
});

it("can add a message when a SSL key is deleted", async () => {
state.sslkey.saved = true;
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter
initialEntries={[
{ pathname: "/account/prefs/ssl-keys", key: "testKey" },
]}
>
<CompatRouter>
<SSLKeyList />
</CompatRouter>
</MemoryRouter>
</Provider>
);
let row = screen.getByRole("row", { name: "ssh-rsa aabb" });

// Click on the delete button:
await userEvent.click(within(row).getByRole("button", { name: "Delete" }));

// Click on the delete confirm button
await userEvent.click(
within(
within(row).getByLabelText(SSLKeyListLabels.DeleteConfirm)
).getByRole("button", {
name: "Delete",
})
);

const actions = store.getActions();
expect(actions.some((action) => action.type === "sslkey/cleanup")).toBe(
true
);
expect(actions.some((action) => action.type === "message/add")).toBe(true);
});

it("displays an empty state message", () => {
state.sslkey.items = [];
renderWithBrowserRouter(<SSLKeyList />, {
Expand Down
Loading

0 comments on commit 44d7ffb

Please sign in to comment.