mirror of
https://github.com/fleetdm/fleet
synced 2026-05-22 00:18:27 +00:00
Fleet UI: Remove install tooltip and add timestamp to InstallDetails modal (#33991)
This commit is contained in:
parent
b4a1671490
commit
f21e9f67b2
8 changed files with 56 additions and 62 deletions
|
|
@ -19,7 +19,7 @@ import SoftwareInstallDetailsModal, {
|
|||
describe("SoftwareInstallDetailsModal", () => {
|
||||
describe("StatusMessage component", () => {
|
||||
it("renders basic 'is installed' message when not installed by fleet (no installResult provided)", () => {
|
||||
render(<StatusMessage softwareName="CoolApp" isDUP={false} />);
|
||||
render(<StatusMessage softwareName="CoolApp" isMyDevicePage={false} />);
|
||||
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/is installed/)).toBeInTheDocument();
|
||||
});
|
||||
|
|
@ -31,7 +31,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
installResult={createMockSoftwareInstallResult({
|
||||
status: "pending_install",
|
||||
})}
|
||||
isDUP={false}
|
||||
isMyDevicePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
expect(screen.getByText(/\(com\.cool\.app\)/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Test Host/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/when it comes online/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/\d+.*ago/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("on device user page, renders failed install with retry option with contact link", () => {
|
||||
|
|
@ -51,7 +52,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
installResult={createMockSoftwareInstallResult({
|
||||
status: "failed_install",
|
||||
})}
|
||||
isDUP
|
||||
isMyDevicePage
|
||||
contactUrl="http://support"
|
||||
/>
|
||||
);
|
||||
|
|
@ -61,6 +62,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
||||
// Host name should not be rendered for device user page
|
||||
expect(screen.queryByText(/Test Host/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/You can retry/)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("link", { name: /contact your IT admin/ })
|
||||
|
|
@ -74,7 +76,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
installResult={createMockSoftwareInstallResult({
|
||||
status: "failed_install",
|
||||
})}
|
||||
isDUP
|
||||
isMyDevicePage
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -83,6 +85,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
||||
// Host name should not be rendered for device user page
|
||||
expect(screen.queryByText(/Test Host/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/You can retry/)).toBeInTheDocument();
|
||||
// Don't show link of not provided
|
||||
expect(
|
||||
|
|
@ -97,7 +100,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
installResult={createMockSoftwareInstallResult({
|
||||
status: "failed_install",
|
||||
})}
|
||||
isDUP={false}
|
||||
isMyDevicePage={false}
|
||||
contactUrl="http://support"
|
||||
/>
|
||||
);
|
||||
|
|
@ -106,6 +109,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
expect(screen.getByText(/failed to install/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Test Host/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/You can retry/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("on host details page/install activity, renders installed message with timestamp", () => {
|
||||
|
|
@ -115,7 +119,7 @@ describe("SoftwareInstallDetailsModal", () => {
|
|||
installResult={createMockSoftwareInstallResult({
|
||||
status: "installed",
|
||||
})}
|
||||
isDUP={false}
|
||||
isMyDevicePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export const renderContactOption = (url?: string) => (
|
|||
interface IInstallStatusMessage {
|
||||
softwareName: string;
|
||||
installResult?: ISoftwareInstallResult;
|
||||
isDUP: boolean;
|
||||
isMyDevicePage: boolean;
|
||||
contactUrl?: string;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ interface IInstallStatusMessage {
|
|||
export const StatusMessage = ({
|
||||
softwareName,
|
||||
installResult,
|
||||
isDUP,
|
||||
isMyDevicePage,
|
||||
contactUrl,
|
||||
}: IInstallStatusMessage) => {
|
||||
// the case when software is installed by the user and not by Fleet
|
||||
|
|
@ -107,22 +107,25 @@ export const StatusMessage = ({
|
|||
Fleet {getInstallDetailsStatusPredicate(status)} <b>{software_title}</b>
|
||||
</>
|
||||
);
|
||||
let middle = null;
|
||||
if (isDUP) {
|
||||
if (status === "failed_install") {
|
||||
middle = <>. You can retry{renderContactOption(contactUrl)}</>;
|
||||
}
|
||||
} else {
|
||||
// host details page
|
||||
middle = (
|
||||
<>
|
||||
{" "}
|
||||
({software_package}) on {formattedHost}
|
||||
{status === "pending_install" ? " when it comes online" : ""}
|
||||
{displayTimeStamp}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const middle = isMyDevicePage ? (
|
||||
<>
|
||||
{" "}
|
||||
{displayTimeStamp}
|
||||
{status === "failed_install" && (
|
||||
<>. You can retry{renderContactOption(contactUrl)}</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{" "}
|
||||
({software_package}) on {formattedHost}
|
||||
{status === "pending_install"
|
||||
? " when it comes online"
|
||||
: displayTimeStamp}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<span>
|
||||
{prefix}
|
||||
|
|
@ -349,7 +352,7 @@ export const SoftwareInstallDetailsModal = ({
|
|||
<StatusMessage
|
||||
installResult={installResultWithHostDisplayName}
|
||||
softwareName={hostSoftware?.name || "Software"} // will always be defined at this point
|
||||
isDUP={!!deviceAuthToken}
|
||||
isMyDevicePage={!!deviceAuthToken}
|
||||
contactUrl={contactUrl}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => {
|
|||
status="pending_uninstall"
|
||||
softwareName="CoolApp"
|
||||
softwarePackageName="com.cool.app"
|
||||
isDUP={false}
|
||||
isMyDevicePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => {
|
|||
hostDisplayName="Test Host"
|
||||
status="failed_uninstall"
|
||||
softwareName="CoolApp"
|
||||
isDUP
|
||||
isMyDevicePage
|
||||
contactUrl="http://support"
|
||||
/>
|
||||
);
|
||||
|
|
@ -54,7 +54,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => {
|
|||
hostDisplayName="Test Host"
|
||||
status="failed_uninstall"
|
||||
softwareName="CoolApp"
|
||||
isDUP={false}
|
||||
isMyDevicePage={false}
|
||||
contactUrl="http://support"
|
||||
/>
|
||||
);
|
||||
|
|
@ -74,7 +74,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => {
|
|||
softwareName="CoolApp"
|
||||
softwarePackageName="com.cool.app"
|
||||
timestamp="2025-08-10T12:00:00Z"
|
||||
isDUP={false}
|
||||
isMyDevicePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ interface IUninstallStatusMessage {
|
|||
softwareName: string;
|
||||
softwarePackageName?: string;
|
||||
timestamp?: string;
|
||||
isDUP: boolean;
|
||||
isMyDevicePage: boolean;
|
||||
contactUrl?: string;
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ export const StatusMessage = ({
|
|||
softwareName,
|
||||
softwarePackageName,
|
||||
timestamp,
|
||||
isDUP,
|
||||
isMyDevicePage,
|
||||
contactUrl,
|
||||
}: IUninstallStatusMessage) => {
|
||||
const formattedHost = hostDisplayName ? <b>{hostDisplayName}</b> : "the host";
|
||||
|
|
@ -67,7 +67,7 @@ export const StatusMessage = ({
|
|||
</>
|
||||
);
|
||||
let suffix = null;
|
||||
if (isDUP) {
|
||||
if (isMyDevicePage) {
|
||||
if (status === "failed_uninstall") {
|
||||
suffix = <>. You can retry{renderContactOption(contactUrl)}</>;
|
||||
}
|
||||
|
|
@ -232,7 +232,7 @@ const SoftwareUninstallDetailsModal = ({
|
|||
softwareName={softwareName}
|
||||
softwarePackageName={softwarePackageName}
|
||||
timestamp={uninstallResult?.created_at}
|
||||
isDUP={!!deviceAuthToken}
|
||||
isMyDevicePage={!!deviceAuthToken}
|
||||
contactUrl={contactUrl}
|
||||
/>
|
||||
{uninstallStatus !== "pending_uninstall" && (
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ describe("getStatusMessage helper function", () => {
|
|||
it("on the device user page, does not show host info", () => {
|
||||
render(
|
||||
getStatusMessage({
|
||||
isDUP: true,
|
||||
isMyDevicePage: true,
|
||||
displayStatus: "installed",
|
||||
isMDMStatusNotNow: false,
|
||||
isMDMStatusAcknowledged: false,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import {
|
|||
} from "../constants";
|
||||
|
||||
interface IGetStatusMessageProps {
|
||||
isDUP?: boolean;
|
||||
isMyDevicePage?: boolean;
|
||||
/** "pending" is an edge case here where VPP install activities that were added to the feed prior to v4.57
|
||||
* (when we split pending into pending_install/pending_uninstall) will list the status as "pending" rather than "pending_install" */
|
||||
displayStatus: SoftwareInstallStatus | "pending";
|
||||
|
|
@ -41,7 +41,7 @@ interface IGetStatusMessageProps {
|
|||
}
|
||||
|
||||
export const getStatusMessage = ({
|
||||
isDUP = false,
|
||||
isMyDevicePage = false,
|
||||
displayStatus,
|
||||
isMDMStatusNotNow,
|
||||
isMDMStatusAcknowledged,
|
||||
|
|
@ -79,7 +79,7 @@ export const getStatusMessage = ({
|
|||
return (
|
||||
<>
|
||||
Fleet tried to install <b>{appName}</b>
|
||||
{!isDUP && (
|
||||
{!isMyDevicePage && (
|
||||
<>
|
||||
{" "}
|
||||
on {formattedHost} but couldn't because the host was locked or
|
||||
|
|
@ -96,9 +96,9 @@ export const getStatusMessage = ({
|
|||
return (
|
||||
<>
|
||||
The MDM command (request) to install <b>{appName}</b>
|
||||
{!isDUP && <> on {formattedHost}</>} was acknowledged but the
|
||||
{!isMyDevicePage && <> on {formattedHost}</>} was acknowledged but the
|
||||
installation has not been verified. To re-check, select <b>Refetch</b>
|
||||
{!isDUP && " for this host"}.
|
||||
{!isMyDevicePage && " for this host"}.
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ export const getStatusMessage = ({
|
|||
return (
|
||||
<>
|
||||
The MDM command (request) to install <b>{appName}</b>
|
||||
{!isDUP && <> on {formattedHost}</>} was acknowledged but the
|
||||
{!isMyDevicePage && <> on {formattedHost}</>} was acknowledged but the
|
||||
installation has not been verified. Please re-attempt this installation.
|
||||
</>
|
||||
);
|
||||
|
|
@ -119,7 +119,7 @@ export const getStatusMessage = ({
|
|||
return (
|
||||
<>
|
||||
The MDM command (request) to install <b>{appName}</b>
|
||||
{!isDUP && <> on {formattedHost}</>} failed
|
||||
{!isMyDevicePage && <> on {formattedHost}</>} failed
|
||||
{displayTimeStamp && <> {displayTimeStamp}</>}. Please re-attempt this
|
||||
installation.
|
||||
</>
|
||||
|
|
@ -127,7 +127,7 @@ export const getStatusMessage = ({
|
|||
}
|
||||
|
||||
const renderSuffix = () => {
|
||||
if (isDUP) {
|
||||
if (isMyDevicePage) {
|
||||
return <> {displayTimeStamp && <> {displayTimeStamp}</>}</>;
|
||||
}
|
||||
return (
|
||||
|
|
@ -299,7 +299,7 @@ export const VppInstallDetailsModal = ({
|
|||
: true; // if no hostSoftware passed in, can assume this is the activity feed, meaning this can only refer to a Fleet-handled install
|
||||
|
||||
const statusMessage = getStatusMessage({
|
||||
isDUP: !!deviceAuthToken,
|
||||
isMyDevicePage: !!deviceAuthToken,
|
||||
displayStatus,
|
||||
isMDMStatusNotNow,
|
||||
isMDMStatusAcknowledged,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ jest.mock("lodash", () => ({
|
|||
const testSoftwarePackage = createMockHostSoftwarePackage();
|
||||
|
||||
describe("InstallStatusCell - component", () => {
|
||||
it("renders 'Installed' status with tooltip", async () => {
|
||||
it("renders 'Installed' status without tooltip", async () => {
|
||||
const { user } = renderWithSetup(
|
||||
<InstallStatusCell
|
||||
software={{
|
||||
|
|
@ -43,9 +43,10 @@ describe("InstallStatusCell - component", () => {
|
|||
|
||||
await user.hover(screen.getByText("Installed"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Software was installed/i)).toBeInTheDocument();
|
||||
});
|
||||
// No tooltip on install status
|
||||
expect(
|
||||
screen.queryByText(/Software was installed/i)
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// There SHOULD be a button with this label
|
||||
expect(
|
||||
|
|
|
|||
|
|
@ -74,21 +74,7 @@ export const INSTALL_STATUS_DISPLAY_OPTIONS: Record<
|
|||
installed: {
|
||||
iconName: "success",
|
||||
displayText: "Installed",
|
||||
tooltip: ({ isSelfService, isAppStoreApp, lastInstalledAt }) => {
|
||||
if (!lastInstalledAt) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
Software was installed{" "}
|
||||
{!isSelfService &&
|
||||
!isAppStoreApp &&
|
||||
"(install script finished with exit code 0) "}
|
||||
{dateAgo(lastInstalledAt)}.
|
||||
</>
|
||||
);
|
||||
},
|
||||
tooltip: () => undefined, // No tooltip for installed state
|
||||
},
|
||||
recently_updated: {
|
||||
iconName: "success",
|
||||
|
|
|
|||
Loading…
Reference in a new issue