Skip to content

Commit

Permalink
Add info dialog & login/logout for provider (#212)
Browse files Browse the repository at this point in the history
* Add ProviderInfoDialog component

* Add useProviderInfoDialog hook

* Add Info button to topbar

* Add providerInfoDialog to app

* Add login/logout provider button

* Fix login/logout provider

* Improve add/remove provider

* Improve CertificatesTopbar

* Fix and add tests for CertificatesTopbar

* Fix certificate list loading skeleton

* Fix dialogs display

* Add more properties to ProviderInfoDialog

* Add isRemovable propertie to ProviderInfoDialog

* Refactoring useApp hook

* Closes the dialogs belonging to the extracted token

* Disable the "Delete Certificate" button when you are not logged in

* Add certificate with privat key icon to certificate list item

* Add title to copy certificate button in certificates list

* Add certificate with privat key label to certificate list item

* Change information button text

* Add toast when provider doesn’t support signing in

* Add action buttons overlay for certificate list item

* Hide "New" & "Delete" certificate buttons when provider is readOnly

* Add tests for ProviderInfoDialog component

* Improve CertificatesTopbar tests

* Add test for useProviderInfoDialog hook

* Add tests for CertificateDeleteDialog component

* Add test for useCertificateDeleteDialog hook

* Add tests for CertificateViewerDialog component

* Add test for useCertificateViewerDialog hook

* Change version

* Fix certificate type label

* Fix certificate creation button

* Fix certificate creation button

* Fix reload provider

* Fix tests

* Change version

* Fix get data

* Fix certificate type label styles

* Remove console

* Change version

---------

Co-authored-by: alex-slobodian <[email protected]>
  • Loading branch information
OleksandrSPV and aleksandr-slobodian authored Oct 18, 2024
1 parent f1b29b5 commit 695e317
Show file tree
Hide file tree
Showing 31 changed files with 1,244 additions and 137 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@peculiar/fortify-tools",
"homepage": "https://tools.fortifyapp.com",
"version": "2.0.3",
"version": "2.0.5",
"author": "PeculiarVentures Team",
"license": "MIT",
"private": true,
Expand Down
44 changes: 36 additions & 8 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useSortList } from "./hooks/sort-list";
import { useSearchList } from "./hooks/search-list";
import { useCertificateImportDialog } from "./dialogs/certificate-import-dialog";
import { useCertificateCreateDialog } from "./dialogs/certificate-create-dialog";
import { useProviderInfoDialog } from "./dialogs/provider-info-dialog";

import styles from "./app.module.scss";

Expand All @@ -21,13 +22,19 @@ export function App() {
fetching,
challenge,
providers,
currentProviderId,
currentProvider,
certificates,
isCurrentProviderLogedin,
handleCertificatesDataReload,
handleProviderChange,
handleRetryConection,
handleProviderLoginLogout,
handleProviderResetAndRefreshList,
} = useApp();

const currentProviderId = currentProvider?.id;
const isCurrentProviderReadOnly = Boolean(currentProvider?.readOnly);

const {
searchedText,
list: searchedCertificate,
Expand All @@ -38,6 +45,7 @@ export function App() {
open: handleCertificateDeleteDialogOpen,
dialog: certificateDeleteDialog,
} = useCertificateDeleteDialog({
providers,
fortifyClient,
onSuccess: (providerId) => {
handleCertificatesDataReload(providerId);
Expand Down Expand Up @@ -78,7 +86,12 @@ export function App() {
const {
open: handleCertificateViewerDialogOpen,
dialog: certificateViewerDialog,
} = useCertificateViewerDialog();
} = useCertificateViewerDialog({
providers,
});

const { open: handleProviderInfoDialogOpen, dialog: providerInfoDialog } =
useProviderInfoDialog({ providers });

return (
<>
Expand All @@ -92,13 +105,20 @@ export function App() {
</CertificatesSidebar>
<CertificatesTopbar
searchValue={searchedText}
isDisabled={!currentProviderId}
isReadOnly={
isCurrentProviderReadOnly || fetching.certificates === "rejected"
}
className={styles.top_bar}
onSearch={handleSearch}
onImport={handleCertificateImportDialogOpen}
onCreate={handleCertificateCreateDialogOpen}
onReload={() =>
currentProviderId && handleCertificatesDataReload(currentProviderId)
onReload={handleProviderResetAndRefreshList}
onInfo={() =>
currentProvider && handleProviderInfoDialogOpen(currentProvider)
}
isLoggedIn={isCurrentProviderLogedin}
onLoginLogout={handleProviderLoginLogout}
></CertificatesTopbar>
<CertificatesList
currentSortName={currentSortName}
Expand All @@ -110,16 +130,24 @@ export function App() {
onViewDetails={handleCertificateViewerDialogOpen}
loading={!fetching.certificates || fetching.certificates === "pending"}
highlightedText={searchedText}
isLoggedIn={isCurrentProviderLogedin}
isReadOnly={isCurrentProviderReadOnly}
/>
<FetchingStatusOwerlay
fetching={fetching}
challenge={challenge}
onReload={handleRetryConection}
/>
{certificateViewerDialog()}
{certificateDeleteDialog()}
{certificateImportDialog()}
{certificateCreateDialog()}
{providers.length ? (
<>
{certificateViewerDialog()}
{certificateDeleteDialog()}
{certificateImportDialog()}
{certificateCreateDialog()}
{providerInfoDialog()}
</>
) : null}

<div className={styles.certificate_list_corners_backdrop}></div>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CertificateDeleteDialog } from "./CertificateDeleteDialog";
const meta: Meta<typeof CertificateDeleteDialog> = {
title: "Components/CertificateDeleteDialog",
component: CertificateDeleteDialog,
tags: ["!autodocs"],
};

export default meta;
Expand All @@ -15,11 +16,3 @@ export const Default: Story = {
certificateId: "12345",
},
};

export const Loading: Story = {
args: {
certificateName: "Certificate Name",
certificateId: "12345",
loading: true,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ComponentProps } from "react";
import { render, userEvent, screen } from "@testing";
import { CertificateDeleteDialog } from "./CertificateDeleteDialog";

describe("<CertificateDeleteDialog />", () => {
const defaultProps: ComponentProps<typeof CertificateDeleteDialog> = {
certificateName: "Certificate Name",
certificateId: "1",
onDialogClose: vi.fn(),
onDeleteClick: vi.fn((data) => data),
};

it("Should render and handle buttons click", async () => {
render(<CertificateDeleteDialog {...defaultProps} />);

expect(
screen.getByText(
`Are you sure you want to delete “${defaultProps.certificateName}”?`
)
).toBeInTheDocument();

await userEvent.click(screen.getByRole("button", { name: /Cancel/ }));

expect(defaultProps.onDialogClose).toBeCalledTimes(1);

await userEvent.click(screen.getByRole("button", { name: /Delete/ }));

expect(defaultProps.onDeleteClick).toBeCalledTimes(1);
expect(defaultProps.onDeleteClick).toBeCalledWith(
defaultProps.certificateId
);
});

it("Should render loading", () => {
render(<CertificateDeleteDialog {...defaultProps} loading={true} />);

expect(screen.getByText(/Deleting certificate/)).toBeInTheDocument();
});
});
30 changes: 24 additions & 6 deletions src/components/certificate-type-label/CertificateTypeLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,48 @@ import { ICertificate } from "@peculiar/fortify-client-core";
import { useTranslation } from "react-i18next";
import clsx from "clsx";
import { Typography } from "@peculiar/react-components";
import CertificateIcon from "../../icons/certificate.svg?react";
import CertificateIcon from "../../icons/certificate-30.svg?react";
import CertificateWithKeyIcon from "../../icons/certificate-with-key-30.svg?react";
import styles from "./styles/index.module.scss";

interface CertificateTypeLabelProps {
type: ICertificate["type"];
withPrivatKey: boolean;
className?: ComponentProps<"div">["className"];
}

export const CertificateTypeLabel: React.FunctionComponent<
CertificateTypeLabelProps
> = (props) => {
const { type, className } = props;
const { type, className, withPrivatKey } = props;
const { t } = useTranslation();

return (
<div className={clsx(className, styles.certificate_type_label)}>
{type === "x509" ? (
<>
<span className={styles.icon_wrapper}>
<CertificateIcon />
{withPrivatKey ? <CertificateWithKeyIcon /> : <CertificateIcon />}
</span>
<span>
<Typography
variant="s2"
color="black"
className={styles.label_part}
>
{t("certificates.list.cell.certificate")}
</Typography>
{withPrivatKey ? (
<Typography
variant="b2"
color="black"
className={styles.label_part}
>
{" "}
{t("certificates.list.cell.with-privat-key")}
</Typography>
) : undefined}
</span>
<Typography variant="s2" color="black">
{t("certificates.list.cell.certificate")}
</Typography>
</>
) : (
<Typography variant="s2" color="black">
Expand Down
20 changes: 12 additions & 8 deletions src/components/certificate-type-label/styles/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
display: flex;
gap: var(--pv-size-base-2);
align-items: center;
.icon_wrapper {
display: flex;
flex-direction: column;
justify-content: center;
}
.icon_wrapper {
display: flex;
flex-direction: column;
justify-content: center;
color: var(--pv-color-gray-9);

width: var(--pv-size-base-6);
svg {
width: var(--pv-size-base-6);
svg {
width: var(--pv-size-base-6);
height: var(--pv-size-base-6);
}
height: var(--pv-size-base-6);
}
}
.label_part {
display: inline;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { render, userEvent, screen } from "@testing";
import { CertificateViewerDialog } from "./CertificateViewerDialog";
import { CertificateProps } from "../../types";

vi.mock("@peculiar/certificates-viewer-react", () => ({
PeculiarCertificateViewer: () => "x509 certificate viewer component",
PeculiarCsrViewer: () => "CSR certificate viewer component",
}));

describe("<CertificateViewerDialog />", () => {
const certificate = {
raw: new ArrayBuffer(0),
subjectName: "Certificate name",
type: "x509",
label: "Certificate name",
subject: {
commonName: "Certificate name",
},
} as unknown as CertificateProps;

it("Should render as x509 and handle close", async () => {
const onCloseMock = vi.fn();

render(
<CertificateViewerDialog
certificate={certificate}
onClose={onCloseMock}
/>
);

expect(screen.getByText(`“${certificate.label}” details`));
expect(screen.getByText(/x509 certificate viewer component/));

await userEvent.click(screen.getByRole("button", { name: /Cancel/ }));

expect(onCloseMock).toBeCalledTimes(1);
});

it("Should render as CSR", async () => {
render(
<CertificateViewerDialog
certificate={
{ ...certificate, type: "csr" } as unknown as CertificateProps
}
onClose={vi.fn()}
/>
);
expect(screen.getByText(/CSR certificate viewer component/));
});
});
Loading

0 comments on commit 695e317

Please sign in to comment.