Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move SSL key delete form to side panel #5341

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading