Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

DEVPROD-1973: Update public keys table #2174

Merged
merged 5 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions cypress/integration/preferences/public_key_management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ describe("Public Key Management Page", () => {
cy.dataCy("table-key-name").first().should("not.contain", keyName1);
cy.dataCy("table-key-name").first().contains(keyName2);
});
it('Displays "No keys saved. Add a new key to populate the list." when no keys are available', () => {
it("Displays empty message", () => {
cy.dataCy("delete-btn").first().click();
cy.contains("button", "Yes").click();
cy.dataCy("delete-btn").first().click();
cy.contains("button", "Yes").click();
cy.contains("No keys saved. Add a new key to populate the list.");
cy.contains("No keys saved.");
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/pages/preferences/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const Preferences: React.FC = () => {
})
}
>
Manage Public Keys
Public Keys
</SideNavItem>
<SideNavItem
active={tab === PreferencesTabRoutes.NewUI}
Expand Down
133 changes: 5 additions & 128 deletions src/pages/preferences/preferencesTabs/PublicKeysTab.tsx
Original file line number Diff line number Diff line change
@@ -1,123 +1,17 @@
import { useState } from "react";
import { useQuery, useMutation } from "@apollo/client";
import styled from "@emotion/styled";
import Button from "@leafygreen-ui/button";
import Icon from "@leafygreen-ui/icon";
import { Table, Skeleton } from "antd";
import { usePreferencesAnalytics } from "analytics";
import { PlusButton } from "components/Buttons";
import Popconfirm from "components/Popconfirm";
import { WordBreak } from "components/styles";
import { size } from "constants/tokens";
import { useToastContext } from "context/toast";
import {
MyPublicKeysQuery,
MyPublicKeysQueryVariables,
RemovePublicKeyMutation,
RemovePublicKeyMutationVariables,
} from "gql/generated/types";
import { REMOVE_PUBLIC_KEY } from "gql/mutations";
import { MY_PUBLIC_KEYS } from "gql/queries";
import {
EditModal,
EditModalPropsState,
} from "pages/preferences/preferencesTabs/publicKeysTab/EditModal";
import { EditModal, EditModalPropsState } from "./publicKeysTab/EditModal";
import { PublicKeysTable } from "./publicKeysTab/PublicKeysTable";

export const PublicKeysTab: React.FC = () => {
const dispatchToast = useToastContext();
const { sendEvent } = usePreferencesAnalytics();
const [editModalProps, setEditModalProps] = useState<EditModalPropsState>(
defaultEditModalProps
);

const onCancel = () => {
setEditModalProps(defaultEditModalProps);
};
const { data: myKeysData, loading: loadingMyPublicKeys } = useQuery<
MyPublicKeysQuery,
MyPublicKeysQueryVariables
>(MY_PUBLIC_KEYS, {
onError(error) {
dispatchToast.error(
`There was an error fetching your public keys: ${error.message}`
);
},
onCompleted() {},
});
const [removePublicKey, { loading: loadingRemovePublicKey }] = useMutation<
RemovePublicKeyMutation,
RemovePublicKeyMutationVariables
>(REMOVE_PUBLIC_KEY, {
onError(error) {
dispatchToast.error(
`There was an error removing the public key: ${error.message}`
);
},
update(cache, { data }) {
cache.writeQuery<MyPublicKeysQuery, MyPublicKeysQueryVariables>({
query: MY_PUBLIC_KEYS,
data: { myPublicKeys: [...data.removePublicKey] },
});
},
});

const columns = [
{
title: "Name",
dataIndex: "name",
key: "name",
render: (text: string): JSX.Element => (
<WordBreak data-cy="table-key-name">{text}</WordBreak>
),
},
{
title: "Actions",
render: (text: string, { key, name }: PublicKey): JSX.Element => (
<BtnContainer>
<Button
size="small"
data-cy="edit-btn"
leftGlyph={<Icon glyph="Edit" />}
onClick={() => {
setEditModalProps({
initialPublicKey: { key, name },
visible: true,
});
}}
/>
<Popconfirm
align="right"
onConfirm={() => {
sendEvent({ name: "Delete public key" });
removePublicKey({ variables: { keyName: name } });
}}
trigger={
<StyledButton
size="small"
data-cy="delete-btn"
disabled={loadingRemovePublicKey}
>
<Icon glyph="Trash" />
</StyledButton>
}
>
Delete this public key?
</Popconfirm>
</BtnContainer>
),
},
];

const tableData = myKeysData?.myPublicKeys ?? [];
const table = tableData.length ? (
<Table
rowKey={({ name }) => name}
columns={columns}
dataSource={tableData}
pagination={false}
/>
) : (
"No keys saved. Add a new key to populate the list."
);

return (
<div>
Expand All @@ -131,25 +25,16 @@ export const PublicKeysTab: React.FC = () => {
});
}}
>
Add New Key
Add key
</PlusButton>
<TableContainer>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could possibly remove the TableContainer here, the space between the button and table seems like a lot

{loadingMyPublicKeys ? (
<Skeleton active title={false} paragraph={{ rows: 4 }} />
) : (
table
)}
<PublicKeysTable setEditModalProps={setEditModalProps} />
</TableContainer>
<EditModal {...editModalProps} onCancel={onCancel} />
</div>
);
};

interface PublicKey {
name: string;
key: string;
}

const defaultEditModalProps = {
visible: false,
initialPublicKey: null,
Expand All @@ -158,11 +43,3 @@ const defaultEditModalProps = {
const TableContainer = styled.div`
margin-top: 48px;
`;

const StyledButton = styled(Button)`
margin-left: ${size.xs};
`;

const BtnContainer = styled.div`
white-space: nowrap;
`;
17 changes: 3 additions & 14 deletions src/pages/preferences/preferencesTabs/publicKeysTab/EditModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ export const EditModal: React.FC<EditModalProps> = ({
`There was an error editing the public key: ${error.message}`
);
},
onCompleted() {},
update(cache, { data }) {
cache.writeQuery<MyPublicKeysQuery, MyPublicKeysQueryVariables>({
query: MY_PUBLIC_KEYS,
data: { myPublicKeys: [...data.updatePublicKey] },
});
},
refetchQueries: ["MyPublicKeys"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:D

});

const [createPublicKey] = useMutation<
CreatePublicKeyMutation,
CreatePublicKeyMutationVariables
Expand All @@ -70,13 +65,7 @@ export const EditModal: React.FC<EditModalProps> = ({
`There was an error creating the public key: ${error.message}`
);
},
onCompleted() {},
update(cache, { data }) {
cache.writeQuery<MyPublicKeysQuery, MyPublicKeysQueryVariables>({
query: MY_PUBLIC_KEYS,
data: { myPublicKeys: [...data.createPublicKey] },
});
},
refetchQueries: ["MyPublicKeys"],
});

const [keyName, setKeyName] = useState<string>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { useRef } from "react";
import { useQuery, useMutation } from "@apollo/client";
import styled from "@emotion/styled";
import Button from "@leafygreen-ui/button";
import Icon from "@leafygreen-ui/icon";
import { useLeafyGreenTable } from "@leafygreen-ui/table";
import { usePreferencesAnalytics } from "analytics";
import Popconfirm from "components/Popconfirm";
import { WordBreak } from "components/styles";
import { BaseTable } from "components/Table/BaseTable";
import { TablePlaceholder } from "components/Table/TablePlaceholder";
import { size } from "constants/tokens";
import { useToastContext } from "context/toast";
import {
MyPublicKeysQuery,
MyPublicKeysQueryVariables,
PublicKey,
RemovePublicKeyMutation,
RemovePublicKeyMutationVariables,
} from "gql/generated/types";
import { REMOVE_PUBLIC_KEY } from "gql/mutations";
import { MY_PUBLIC_KEYS } from "gql/queries";
import { EditModalPropsState } from "./EditModal";

type PublicKeysTableProps = {
setEditModalProps: React.Dispatch<React.SetStateAction<EditModalPropsState>>;
};

export const PublicKeysTable: React.FC<PublicKeysTableProps> = ({
setEditModalProps,
}) => {
const dispatchToast = useToastContext();
const { sendEvent } = usePreferencesAnalytics();

const { data: myKeysData, loading: loadingMyPublicKeys } = useQuery<
MyPublicKeysQuery,
MyPublicKeysQueryVariables
>(MY_PUBLIC_KEYS, {
onError(error) {
dispatchToast.error(
`There was an error fetching your public keys: ${error.message}`
);
},
});

const [removePublicKey, { loading: loadingRemovePublicKey }] = useMutation<
RemovePublicKeyMutation,
RemovePublicKeyMutationVariables
>(REMOVE_PUBLIC_KEY, {
onError(error) {
dispatchToast.error(
`There was an error removing the public key: ${error.message}`
);
},
refetchQueries: ["MyPublicKeys"],
});

const columns = [
{
header: "Name",
accessorKey: "name",
cell: ({ getValue }) => (
<WordBreak data-cy="table-key-name">{getValue()}</WordBreak>
),
},
{
header: "Actions",
cell: ({ row }) => {
const { key, name } = row.original;
return (
<ButtonContainer className="w-[100px]">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just double checking that you mean to keep this className here

<Button
size="small"
data-cy="edit-btn"
leftGlyph={<Icon glyph="Edit" />}
onClick={() => {
setEditModalProps({
initialPublicKey: { key, name },
visible: true,
});
}}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly could look nice if we turned these into IconButtons? its just a suggestion :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alas it looked a bit weird :( we use this action button pattern elsewhere too so I think the consistency is good!

<Popconfirm
align="right"
onConfirm={() => {
sendEvent({ name: "Delete public key" });
removePublicKey({ variables: { keyName: name } });
}}
trigger={
<Button
size="small"
data-cy="delete-btn"
disabled={loadingRemovePublicKey}
>
<Icon glyph="Trash" />
</Button>
}
>
Delete this public key?
</Popconfirm>
</ButtonContainer>
);
},
},
];

const tableContainerRef = useRef<HTMLDivElement>(null);
const table = useLeafyGreenTable<PublicKey>({
columns,
containerRef: tableContainerRef,
data: myKeysData?.myPublicKeys ?? [],
defaultColumn: {
// Workaround for react-table auto sizing limitations.
// https://github.com/TanStack/table/discussions/4179#discussioncomment-7142606
size: "auto" as unknown as number,
},
});

return (
<BaseTable
data-cy="hosts-table"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data-cy should probably be updated!

emptyComponent={<TablePlaceholder glyph="Key" message="No keys saved." />}
loading={loadingMyPublicKeys}
shouldAlternateRowColor
table={table}
/>
);
};

const ButtonContainer = styled.div`
display: flex;
gap: ${size.xs};
`;