UI - Delete secret modal: Close modal on error/success, truncate long var names, add missing copy, fix copy alignment (#32508)

## For #32465 

Modal closes when delete errors:
<img width="1350" height="877" alt="Screenshot 2025-09-02 at 10 20
35 AM"
src="https://github.com/user-attachments/assets/daa97771-ba2f-49a7-9324-a2ce3f5bbe46"
/>

and when it succeeds:
<img width="1350" height="877" alt="Screenshot 2025-09-02 at 10 22
01 AM"
src="https://github.com/user-attachments/assets/94404529-bf8c-4c3a-bd0f-af3bab8bdc35"
/>

Short variable names render inline, additional copy underneath:
<img width="1074" height="610" alt="Screenshot 2025-09-02 at 11 33
16 AM"
src="https://github.com/user-attachments/assets/61958099-4450-4e2f-9ee8-f6ed15be8f2b"
/>

While long ones are truncated with the full value displayed in a
tooltip, also inline with additional copy underneath:
<img width="1074" height="610" alt="Screenshot 2025-09-02 at 11 33
55 AM"
src="https://github.com/user-attachments/assets/1fb45cb1-9252-45c9-a62e-c39cae05caaa"
/>

- [x] QA'd all new/changed functionality manually
- [x] Updated tests
- [x] Confirmed that the fix is not expected to adversely impact load
test results

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
jacobshandling 2025-09-02 13:55:47 -06:00 committed by GitHub
parent 3a6ef84b82
commit 669aaa7f2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 44 additions and 30 deletions

View file

@ -205,14 +205,7 @@ describe("Custom variables", () => {
expect(
screen.getByText(/Delete custom variable\?/)
).toBeInTheDocument();
expect(
screen.getByText((content, element) => {
return (
element?.textContent ===
"This will delete the SECRET_UNO custom variable."
);
})
).toBeInTheDocument();
expect(screen.getByText(/This will delete the/)).toBeInTheDocument();
});
await new Promise((resolve) => setTimeout(resolve, 250));
await user.click(screen.getByRole("button", { name: "Delete" }));
@ -354,14 +347,7 @@ describe("Custom variables", () => {
expect(
screen.getByText(/Delete custom variable\?/)
).toBeInTheDocument();
expect(
screen.getByText((content, element) => {
return (
element?.textContent ===
"This will delete the SECRET_UNO custom variable."
);
})
).toBeInTheDocument();
expect(screen.getByText(/This will delete the/)).toBeInTheDocument();
});
await new Promise((resolve) => setTimeout(resolve, 250));
await user.click(screen.getByRole("button", { name: "Delete" }));

View file

@ -105,9 +105,8 @@ const Secrets = () => {
setShowDeleteModal(true);
};
const onDeleteSecret = () => {
const reloadList = () => {
paginatedListRef.current?.reload();
setShowDeleteModal(false);
};
const getTokenFromSecretName = (secretName: string): string => {
@ -277,8 +276,8 @@ const Secrets = () => {
{showDeleteModal && (
<DeleteSecretModal
secret={secretToDelete}
onCancel={() => setShowDeleteModal(false)}
onDelete={onDeleteSecret}
onExit={() => setShowDeleteModal(false)}
reloadList={reloadList}
/>
)}
</div>

View file

@ -1,6 +1,7 @@
import React, { useContext, useState } from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
import TooltipTruncatedText from "components/TooltipTruncatedText";
import { ISecret } from "interfaces/secrets";
import { NotificationContext } from "context/notification";
@ -9,16 +10,16 @@ import secretsAPI from "services/entities/secrets";
interface DeleteSecretModalProps {
secret: ISecret | undefined;
onCancel: () => void;
onDelete: () => void;
onExit: () => void;
reloadList: () => void;
}
const baseClass = "fleet-delete-secret-modal";
const DeleteSecretModal = ({
secret,
onCancel,
onDelete,
onExit,
reloadList,
}: DeleteSecretModalProps) => {
const [isDeleting, setIsDeleting] = useState(false);
@ -32,7 +33,7 @@ const DeleteSecretModal = ({
try {
await secretsAPI.deleteSecret(secret.id);
renderFlash("success", "Variable successfully deleted.");
onDelete();
reloadList();
} catch (error) {
const errorObject = formatErrorResponse(error);
const isInUseError =
@ -45,19 +46,31 @@ const DeleteSecretModal = ({
renderFlash("error", message);
} finally {
setIsDeleting(false);
onExit();
}
};
return (
<Modal
title="Delete custom variable?"
onExit={onCancel}
onExit={onExit}
className={baseClass}
>
<>
<p>
This will delete the <b>{secret?.name}</b> custom variable.
</p>
<div className={`${baseClass}__message`}>
<span>
This will delete the
<b>
<TooltipTruncatedText value={secret?.name} />
</b>
custom variable.
</span>
<br />
<br />
If this custom variable is used in any configuration profiles or
scripts, they will fail. To resolve, edit the configuration profile or
script.
</div>
<div className="modal-cta-wrap">
<Button
variant="alert"
@ -67,7 +80,7 @@ const DeleteSecretModal = ({
>
Delete
</Button>
<Button variant="inverse-alert" onClick={onCancel}>
<Button variant="inverse-alert" onClick={onExit}>
Cancel
</Button>
</div>

View file

@ -0,0 +1,16 @@
.fleet-delete-secret-modal {
&__message {
span {
display: inline-flex;
gap: 4px;
align-items: baseline;
.tooltip-truncated-text {
max-width: 322px; // this specific max-width facilitates the desired truncation behavior within the inline parent
.__react_component_tooltip {
font-weight: $regular;
}
}
}
}
}